library(tidyverse)
library(sf)
library(raster)
library(knitr)
library(kableExtra)
library(tidycensus)
library(tigris)
library(FNN)
#library(QuantPsyc) # JE Note: in R 4.1, QuantPsyc package not available.
library(caret)
library(yardstick)
library(pscl)
library(plotROC) 
library(ggrepel)
library(pROC)
library(grid)
library(gridExtra)
library(viridis)
library(igraph)

plotTheme <- theme(
  plot.title =element_text(size=12),
  plot.subtitle = element_text(size=8),
  plot.caption = element_text(size = 6),
  axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
  axis.text.y = element_text(size = 10),
  axis.title.y = element_text(size = 10),
  # Set the entire chart region to blank
  panel.background=element_blank(),
  plot.background=element_blank(),
  #panel.border=element_rect(colour="#F0F0F0"),
  # Format the grid
  panel.grid.major=element_line(colour="#D0D0D0",size=.75),
  axis.ticks=element_blank())

mapTheme <- theme(plot.title =element_text(size=12),
                  plot.subtitle = element_text(size=8),
                  plot.caption = element_text(size = 6),
                  axis.line=element_blank(),
                  axis.text.x=element_blank(),
                  axis.text.y=element_blank(),
                  axis.ticks=element_blank(),
                  axis.title.x=element_blank(),
                  axis.title.y=element_blank(),
                  panel.background=element_blank(),
                  panel.border=element_blank(),
                  panel.grid.major=element_line(colour = 'transparent'),
                  panel.grid.minor=element_blank(),
                  legend.direction = "vertical", 
                  legend.position = "right",
                  plot.margin = margin(1, 1, 1, 1, 'cm'),
                  legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))

palette2 <- c("#41b6c4","#253494")
palette4 <- c("#a1dab4","#41b6c4","#2c7fb8","#253494")
palette5 <- c("#ffffcc","#a1dab4","#41b6c4","#2c7fb8","#253494")
palette10 <- c("#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4",
               "#4eb3d3","#2b8cbe","#0868ac","#084081","#f7fcf0")
#this function converts a column in to quintiles. It is used for mapping.
quintileBreaks <- function(df,variable) {
    as.character(quantile(df[[variable]],
                          c(.01,.2,.4,.6,.8),na.rm=T))
}

#This function can be used to convert a polygon sf to centroids xy coords.
xyC <- function(aPolygonSF) {
  as.data.frame(
    cbind(x=st_coordinates(st_centroid(aPolygonSF))[,1],
          y=st_coordinates(st_centroid(aPolygonSF))[,2]))
} 

#this function convert a raster to a data frame so it can be plotted in ggplot
rast <- function(inRaster) {
  data.frame(
    xyFromCell(inRaster, 1:ncell(inRaster)), 
    value = getValues(inRaster)) }

1: Set up

dvMSA <- 
  st_read("/Users/luyiiwong/Documents/Land Use & Environmental Modeling/Assignment5/dv_counties/dv_counties.shp") 

lc_2011 = raster("/Users/luyiiwong/Documents/Land Use & Environmental Modeling/Assignment5/lc_2011_clip_Resample.tif")
lc_2021 = raster("/Users/luyiiwong/Documents/Land Use & Environmental Modeling/Assignment5/lc_2021_clip_Resample.tif")

lc_change <- lc_2011+lc_2021

2: Calculating Land Cover Change

#creating a matrix to classify urban and non-urban land use
reclassMatrix <- 
  matrix(c(
    0,12,0,
    12,24,1,
    24,Inf,0),
  ncol=3, byrow=T)
# reclassifying land cover for both years
developed_2011 <- 
  reclassify(lc_2011,reclassMatrix)

developed_2021 <- 
  reclassify(lc_2021,reclassMatrix)
# calculating land use change from 2011 to 2021
development_change <- developed_2011+developed_2021

# the values that are =1 indicate that there was CHANGE between 2011 and 2021
# histogram shows that there were very few values in the study area
hist(development_change)

# reclassifying values that are not equal to 1 to NA in development change
# this means that values 0 and 2 become NA
development_change[development_change != 1] <- NA

# plotting the values of development change = 1 spatially
ggplot() +
  geom_sf(data=dvMSA) +
  geom_raster(data=rast(development_change) %>% na.omit, 
              aes(x,y,fill=as.factor(value))) +
  scale_fill_viridis(discrete=TRUE, name ="Land Cover\nChange") + 
  labs(title="Development land use change") +
  mapTheme

2.1: Creating the fishnet

dvMSA_fishnet <- 
  st_make_grid(dvMSA, 500) %>%
  st_sf()

dvMSA_fishnet <-
  dvMSA_fishnet[dvMSA,]
ggplot() +
  geom_sf(data=dvMSA_fishnet) +
  labs(title="Fishnet, 500 Foot Resolution") +
  mapTheme

#original version
changePoints <-
  rasterToPoints(development_change) %>%
  as.data.frame() %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(dvMSA_fishnet))

# changed this naming from lc_change to layer then converted NA to 0
# because in fishnet here, it's called layer not lc_change
fishnet <- 
  aggregate(changePoints, dvMSA_fishnet, sum) %>%
  mutate(layer = ifelse(is.na(layer),0,1),
         layer = as.factor(layer))

ggplot() +
  geom_sf(data=dvMSA) +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)$x, y=xyC(fishnet)$y, colour=layer)) +
  scale_colour_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name = "") +
  labs(title = "Land Cover Development Change", subtitle = "As fishnet centroids") +
  mapTheme

2.2: Plotting Land Cover in 2011

ggplot() +
  geom_sf(data=dvMSA) +
  geom_raster(data=rast(lc_2011) %>% na.omit %>% filter(value > 0), 
              aes(x,y,fill=as.factor(value))) +
  scale_fill_viridis(discrete=TRUE, name ="") +
  labs(title = "Land Cover, 2011") +
  mapTheme +
  theme(legend.direction="horizontal")

The table below shows the approach taken to recoded existing land cover codes into the categories used in our analysis. In the code block below new rasters are generated and names are applied. Naming ensures that when the raster is integrated with the fishnet, the column reflects the appropriate raster.

Old_Classification New_Classification
Open Space as well as Low, Medium and High Intensity Development Developed
Deciduous, Evergreen, and Mixed Forest Forest
Pasture/Hay and Cultivated Crops Farm
Woody and Emergent Herbaceous Wetlands Woodlands
Barren Land, Dwarf Scrub, and Grassland/Herbaceous Other Undeveloped
Water Water
developed <- lc_2011 == 21 | lc_2011 == 22 | lc_2011 == 23 | lc_2011 == 24
forest <- lc_2011 == 41 | lc_2011 == 42 | lc_2011 == 43 
farm <- lc_2011 == 81 | lc_2011 == 82 
wetlands <- lc_2011 == 90 | lc_2011 == 95 
otherUndeveloped <- lc_2011 == 52 | lc_2011 == 71 | lc_2011 == 31 
water <- lc_2011 == 11

names(developed) <- "developed"
names(forest) <- "forest"
names(farm) <- "farm"
names(wetlands) <- "wetlands"
names(otherUndeveloped) <- "otherUndeveloped"
names(water) <- "water"
aggregateRaster <- function(inputRasterList, theFishnet) {
  #create an empty fishnet with the same dimensions as the input fishnet
  theseFishnets <- theFishnet %>% dplyr::select()
  #for each raster in the raster list
  for (i in inputRasterList) {
  #create a variable name corresponding to the ith raster
  varName <- names(i)
  #convert raster to points as an sf
    thesePoints <-
      rasterToPoints(i) %>%
      as.data.frame() %>%
      st_as_sf(coords = c("x", "y"), crs = st_crs(theFishnet)) %>%
      filter(.[[1]] == 1)
  #aggregate to the fishnet
    thisFishnet <-
      aggregate(thesePoints, theFishnet, length) %>%
      mutate(!!varName := ifelse(is.na(.[[1]]),0,1))
  #add to the larger fishnet
    theseFishnets <- cbind(theseFishnets,thisFishnet)
  }
  #output all aggregates as one large fishnet
   return(theseFishnets)
  }
theRasterList <- c(developed,forest,farm,wetlands,otherUndeveloped,water)

aggregatedRasters <-
  aggregateRaster(theRasterList, dvMSA_fishnet) %>%
  dplyr::select(developed,forest,farm,wetlands,otherUndeveloped,water) %>%
  mutate_if(is.numeric,as.factor)

aggregatedRasters %>%
  gather(var,value,developed:water) %>%
  st_cast("POLYGON") %>%    #just to make sure no weird geometries slipped in
  mutate(X = xyC(.)$x,
         Y = xyC(.)$y) %>%
  ggplot() +
    geom_sf(data=dvMSA) +
    geom_point(aes(X,Y, colour=as.factor(value))) +
    facet_wrap(~var) +
    scale_colour_manual(values = palette2,
                        labels=c("Other","Land Cover"),
                        name = "") +
    labs(title = "Land Cover Types, 2011",
         subtitle = "As fishnet centroids") +
   mapTheme

3: Pulling Demographic Data

# setting up api key 
census_api_key("b83a23afee4a8ed0fa131e449869e6577b87151e", overwrite = TRUE, install = TRUE)

Delaware Valley MSA, Pennsylvania Counties:

  • Bucks County
  • Chester County
  • Delaware County
  • Montgomery County
  • Philadelphia County
# pulling data from census for 2011
dvpop_2011 <- get_acs(geography = "tract", 
          variables = c("B01003_001E"), 
          year = 2011, 
          state = "PA", 
          county = c("Bucks", "Chester", "Delaware", "Montgomery", "Philadelphia"),
          geometry = TRUE, 
          output = "wide") %>%
  rename(pop_2011 = B01003_001E) %>%
  dplyr::select(GEOID, NAME, pop_2011, geometry)%>%
  st_transform(st_crs(dvMSA_fishnet))


# pulling data from census for 2021
dvpop_2021 <- get_acs(geography = "tract", 
          variables = c("B01003_001E"), 
          year = 2021, 
          state = "PA", 
          county = c("Bucks", "Chester", "Delaware", "Montgomery", "Philadelphia"),
          geometry = TRUE, 
          output = "wide") %>%
  rename(pop_2021 = B01003_001E) %>%
  dplyr::select(GEOID, NAME, pop_2021, geometry)%>%
  st_transform(st_crs(dvMSA_fishnet))




## grid arrange tract 2011 v 2021
# WHY are values NA????
grid.arrange(
ggplot() +
  geom_sf(data = dvpop_2011, aes(fill=factor(ntile(pop_2011,5))), colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=quintileBreaks(dvpop_2011,"pop_2011"),
                   name="Quintile\nBreaks") +
  labs(title="Population, Delware Valley PA counties by tract: 2011") +
  mapTheme,

ggplot() +
  geom_sf(data = dvpop_2021, aes(fill=factor(ntile(pop_2021,5))), colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=quintileBreaks(dvpop_2021,"pop_2021"),
                   name="Quintile\nBreaks") +
  labs(title="Population, Delware Valley PA counties by tract: 2021") +
  mapTheme, ncol=2)

3.1: Interpolating Population and Fishnet

dvMSA_fishnet <-
  dvMSA_fishnet %>%
  rownames_to_column("fishnetID") %>% 
  mutate(fishnetID = as.numeric(fishnetID)) %>%
  dplyr::select(fishnetID)

fishnetPopulation11 <-
  st_interpolate_aw(dvpop_2011["pop_2011"], dvMSA_fishnet, extensive=TRUE) %>%
  as.data.frame(.) %>%
  rownames_to_column(var = "fishnetID") %>%
  left_join(dvMSA_fishnet %>%
              mutate(fishnetID = as.character(fishnetID)),
            ., by=c("fishnetID"='fishnetID')) %>% 
  mutate(pop_2011 = replace_na(pop_2011,0)) %>%
  dplyr::select(pop_2011)

fishnetPopulation21 <-
  st_interpolate_aw(dvpop_2021["pop_2021"],dvMSA_fishnet, extensive=TRUE) %>%
  as.data.frame(.) %>%
  rownames_to_column(var = "fishnetID") %>%
  left_join(dvMSA_fishnet %>%
              mutate(fishnetID = as.character(fishnetID)),
            ., by=c("fishnetID"='fishnetID')) %>% 
  mutate(pop_2021 = replace_na(pop_2021,0)) %>%
  dplyr::select(pop_2021)

fishnetPopulation <- 
  cbind(fishnetPopulation11,fishnetPopulation21) %>%
  dplyr::select(pop_2011,pop_2021) %>%
  mutate(pop_Change = pop_2021 - pop_2011)
grid.arrange(
ggplot() +
  geom_sf(data=dvpop_2021, aes(fill=factor(ntile(pop_2021,5))),colour=NA) +
  scale_fill_manual(values = palette5,
                    labels=substr(quintileBreaks(dvpop_2021,"pop_2021"),1,4),
                   name="Quintile\nBreaks") +
  labs(title="Population, Delaware Valley MSA, PA: 2021",
       subtitle="Represented as tracts; Boundaries omitted") +
  mapTheme,

ggplot() +
  geom_sf(data=fishnetPopulation, aes(fill=factor(ntile(pop_2021,5))),colour=NA) +
  scale_fill_manual(values = palette5,
                   labels=substr(quintileBreaks(fishnetPopulation,"pop_2021"),1,4),
                   name="Quintile\nBreaks") +
  labs(title="Population, Delaware Valley MSA, PA: 2021",
       subtitle="Represented as fishnet gridcells; Boundaries omitted") +
  mapTheme, ncol=2)

4: Highway Distance

#dvHighways <-
#  st_read("C:/Users/ferna/OneDrive/Documents/ArcGIS/Projects/CPLN 6750/HW5/dv_roads.geojson") %>%
#  st_transform(st_crs(dvMSA)) %>%
#  st_intersection(dvMSA)

dvHighways <-
  st_read("/Users/luyiiwong/Documents/GitHub/LandUseModeling_HW5/DelawareValley/dv_roads.geojson") %>%
  st_transform(st_crs(dvMSA)) %>%
  st_intersection(dvMSA)
ggplot() +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=layer),size=1.5) +
  geom_sf(data=dvHighways) +
  scale_colour_manual(values = palette2,
                      labels=c("No Change","New Development")) +
  labs(title = "New Development and Highways",
       subtitle = "As fishnet centroids") +
  mapTheme

emptyRaster <- lc_change
emptyRaster[] <- NA

dvHighways_spdf <- as(dvHighways, "Spatial")
highway_raster <- rasterize(dvHighways, emptyRaster)

#highway_raster <- 
  #as(dvHighways,'Spatial') %>%
  #rasterize(.,emptyRaster)

highway_raster_distance <- distance(highway_raster)
names(highway_raster_distance) <- "distance_highways"

highwayPoints <-
  rasterToPoints(highway_raster_distance) %>%
  as.data.frame() %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(dvMSA_fishnet))

highwayPoints_fishnet <- 
  aggregate(highwayPoints, dvMSA_fishnet, mean) %>%
  mutate(distance_highways = ifelse(is.na(distance_highways),0,distance_highways))

ggplot() +
  geom_sf(data=dvMSA) +
  geom_point(data=highwayPoints_fishnet, aes(x=xyC(highwayPoints_fishnet)[,1], 
                                             y=xyC(highwayPoints_fishnet)[,2], 
                 colour=factor(ntile(distance_highways,5))),size=1.5) +
  scale_colour_manual(values = palette5,
                      labels=substr(quintileBreaks(highwayPoints_fishnet,"distance_highways"),1,8),
                      name="Quintile/nBreaks") +
  geom_sf(data=dvHighways, colour = "red") +
  labs(title = "Distance to Highways",
       subtitle = "As fishnet centroids; Highways visualized in red") +
  mapTheme

5: Spatial lag

nn_function <- function(measureFrom,measureTo,k) {
  #convert the sf layers to matrices
  measureFrom_Matrix <-
    as.matrix(measureFrom)
  measureTo_Matrix <-
    as.matrix(measureTo)
  nn <-   
    get.knnx(measureTo, measureFrom, k)$nn.dist
    output <-
    as.data.frame(nn) %>%
    rownames_to_column(var = "thisPoint") %>%
    gather(points, point_distance, V1:ncol(.)) %>%
    arrange(as.numeric(thisPoint)) %>%
    group_by(thisPoint) %>%
    summarize(pointDistance = mean(point_distance)) %>%
    arrange(as.numeric(thisPoint)) %>% 
    dplyr::select(-thisPoint) %>%
    pull()
  
  return(output)  
}
fishnet$lagDevelopment <-
    nn_function(xyC(fishnet),
                xyC(filter(aggregatedRasters,developed==1)),
                2)

ggplot() +
  geom_sf(data=dvMSA) +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2], 
                 colour= log(lagDevelopment), size=.001)) +
  labs(title = "Spatial Lag to 2011 Development",
       subtitle = "As fishnet centroids")

#how should we interpret this?
hist(fishnet$lagDevelopment)

# make histogram
# try viridis
# log color/lagdevelopment
# filter out og developed cells 

6: Create MSA Counties

options(tigris_class = "sf")

studyAreaCounties <- 
  counties("Pennsylvania") %>%
  st_transform(st_crs(dvMSA)) %>%
  dplyr::select(NAME) %>%
  .[st_buffer(dvMSA,-500), , op=st_intersects]
ggplot() +
  geom_sf(data=studyAreaCounties) +
  labs(title = "Study Area Counties") +
  mapTheme

7: Create the Final Dataset

Once we join the data set with out county boundaries, all the lc_change =1 is dropped

# can we go through this code - specifically the mutate function
# making sure logic is right, that change is from non-developed to developed
# basically ignoring classifications that are wrong
dat <- 
  cbind(fishnet, highwayPoints_fishnet, fishnetPopulation, aggregatedRasters)%>%
  dplyr::select(layer, developed, forest, farm, wetlands, otherUndeveloped, water,
                pop_2011, pop_2021, pop_Change, distance_highways,lagDevelopment) %>%
  st_join(studyAreaCounties) %>%
  mutate(developed10 = ifelse(layer == 1 & developed == 1, 0, developed)) %>%
  filter(water == 0) 

8: Exploratroy Analysis

It seems like land change only occured from undeveloped to water. Therefore, there is no new development…

dat %>%
  dplyr::select(distance_highways,lagDevelopment,layer) %>%
  gather(Variable, Value, -layer, -geometry) %>%
  ggplot(., aes(layer, Value, fill=layer)) + 
    geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
    facet_wrap(~Variable) +
    scale_fill_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name="") +
    labs(title="New Development as a Function of the Continuous Variables") +
    plotTheme 

dat %>%
  dplyr::select(pop_2011,pop_2021,pop_Change,layer) %>%
  gather(Variable, Value, -layer, -geometry) %>%
  ggplot(., aes(layer, Value, fill=layer)) + 
    geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
    facet_wrap(~Variable) +
    scale_fill_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name="") +
    labs(title="New Development as a Function of Factor Variables") +
    plotTheme

dat %>%
  dplyr::select(layer:otherUndeveloped,developed) %>%
  gather(Land_Cover_Type, Value, -layer, -geometry) %>%
   st_set_geometry(NULL) %>%
     group_by(layer, Land_Cover_Type) %>%
     summarize(n = sum(as.numeric(Value))) %>%
     ungroup() %>%
    mutate(Conversion_Rate = paste0(round(100 * n/sum(n), 2), "%")) %>%
    filter(layer == 1) %>%
  dplyr::select(Land_Cover_Type,Conversion_Rate) %>%
  kable() %>% kable_styling(full_width = F)
Land_Cover_Type Conversion_Rate
developed 0.01%
farm 0.43%
forest 0.25%
otherUndeveloped 0.05%
wetlands 0.01%

9: Predicting for 2021 Model

set.seed(3456)
trainIndex <- 
  createDataPartition(dat$developed, p = .50,
                                  list = FALSE,
                                  times = 1)
datTrain <- dat[ trainIndex,]
datTest  <- dat[-trainIndex,]
Model1 <- glm(layer ~ wetlands + forest  + farm + otherUndeveloped, 
              family="binomial"(link="logit"), data = datTrain)

Model2 <- glm(layer ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment, 
              family="binomial"(link="logit"), data = datTrain)
              
Model3 <- glm(layer ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment + pop_2011, 
              family="binomial"(link="logit"), data = datTrain)          
              
Model4 <- glm(layer ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment + pop_2011 + 
              pop_2021, 
              family="binomial"(link="logit"), data = datTrain)              
            
Model5 <- glm(layer ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment + pop_Change, 
              family="binomial"(link="logit"), data = datTrain)              
              
Model6 <- glm(layer ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment + pop_Change + 
              distance_highways, 
              family="binomial"(link="logit"), data = datTrain) 
modelList <- paste0("Model", 1:6)
map_dfc(modelList, function(x)pR2(get(x)))[4,] %>%
  setNames(paste0("Model",1:6)) %>%
  gather(Model,McFadden) %>%
  ggplot(aes(Model,McFadden)) +
    geom_bar(stat="identity") +
    labs(title= "McFadden R-Squared by Model") +
    plotTheme

testSetProbs <- 
  data.frame(class = datTest$layer,
             probs = predict(Model6, datTest, type="response")) 
  
ggplot(testSetProbs, aes(probs)) +
  geom_density(aes(fill=class), alpha=0.5) +
  scale_fill_manual(values = palette2,
                    labels=c("No Change","New Development")) +
  labs(title = "Histogram of test set predicted probabilities",
       x="Predicted Probabilities",y="Density") +
  plotTheme

9.1: Accuracy

options(yardstick.event_first = FALSE)

testSetProbs <- 
  testSetProbs %>% 
  mutate(predClass_05 = as.factor(ifelse(testSetProbs$probs >= 0.05 ,1,0)),
         predClass_17 = as.factor(ifelse(testSetProbs$probs >= 0.17 ,1,0))) 

testSetProbs %>%
  dplyr::select(-probs) %>%
  gather(Variable, Value, -class) %>%
  group_by(Variable) %>%
  summarize(Sensitivity = round(yardstick::sens_vec(class,factor(Value)),2),
            Specificity = round(yardstick::spec_vec(class,factor(Value)),2),
            Accuracy = round(yardstick::accuracy_vec(class,factor(Value)),2)) %>% 
  kable() %>%
  kable_styling(full_width = F)
Variable Sensitivity Specificity Accuracy
predClass_05 0.99 0.03 0.99
predClass_17 1.00 0.00 0.99
predsForMap <-         
  dat %>%
    mutate(probs = predict(Model6, dat, type="response") ,
           Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
           Threshold_17_Pct =  as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
    dplyr::select(layer,Threshold_5_Pct,Threshold_17_Pct) %>%
    gather(Variable,Value, -geometry) %>%
    st_cast("POLYGON")
ggplot() +
  geom_point(data=predsForMap, aes(x=xyC(predsForMap)[,1], y=xyC(predsForMap)[,2], colour=Value)) +
  facet_wrap(~Variable) +
  scale_colour_manual(values = palette2, labels=c("No Change","New Development"),
                      name="") +
  labs(title="Development Predictions - Low Threshold") + 
  mapTheme

ConfusionMatrix.metrics <-
  dat %>%
    mutate(probs = predict(Model6, dat, type="response") ,
           Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
           Threshold_17_Pct =  as.factor(ifelse(probs >= 0.17 ,1,0))) %>%
    mutate(TrueP_05 = ifelse(layer  == 1 & Threshold_5_Pct == 1, 1,0),
           TrueN_05 = ifelse(layer  == 0 & Threshold_5_Pct == 0, 1,0),
           TrueP_17 = ifelse(layer  == 1 & Threshold_17_Pct == 1, 1,0),
           TrueN_17 = ifelse(layer  == 0 & Threshold_17_Pct == 0, 1,0)) %>%
    dplyr::select(., starts_with("True")) %>%
    gather(Variable, Value, -geometry) %>%
    st_cast("POLYGON") 
ggplot(data=ConfusionMatrix.metrics) +
  geom_point(aes(x=xyC(ConfusionMatrix.metrics)[,1], 
                 y=xyC(ConfusionMatrix.metrics)[,2], colour = as.factor(Value))) +
  facet_wrap(~Variable) +
  scale_colour_manual(values = palette2, labels=c("Correct","Incorrect"),
                       name="") +
  labs(title="Development Predictions - Low Threshold") + mapTheme

10: Predicting Land Cover Demand for 2031

dat <-
  dat %>%
  mutate(lagDevelopment = nn_function(xyC(.), xyC(filter(.,developed10 == 1)),2))
#CHANGE THIS CODE
countyPopulation_2031 <- 
  data.frame(
   NAME = 
     c("Philadelphia","Chester","Montgomery","Bucks","Delaware"),
   county_projection_2031 = 
     c(1643971, 599932, 884387, 669299, 577248)) %>%
  #CHANGE LINE ABOVE - from a study DVRPC population projection
   left_join(
     dat %>%
       st_set_geometry(NULL) %>%
       group_by(NAME) %>%
       summarize(county_population_2021 = round(sum(pop_2021))))

countyPopulation_2031 %>%
  gather(Variable,Value, -NAME) %>%
  ggplot(aes(reorder(NAME,-Value),Value)) +
  geom_bar(aes(fill=Variable), stat = "identity", position = "dodge") +
  scale_fill_manual(values = palette2,
                    labels=c("2021","2031"),
                    name="Population") +
  labs(title="Population Change by County: 2021 - 2031",
       x="County", y="Population") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  plotTheme

10.1: Predicting Development Demand

# section edited
dat_infill <-
  dat %>%
  #calculate population change
    left_join(countyPopulation_2031) %>%
    mutate(proportion_of_county_pop = pop_2021 / county_population_2021,
           pop_2031.infill = proportion_of_county_pop * county_projection_2031,
           pop_Change = round(pop_2031.infill - pop_2021),2) %>%
    dplyr::select(-county_projection_2031, -county_population_2021, 
                  -proportion_of_county_pop, -pop_2031.infill) %>%
  #predict for 2031
    mutate(predict_2031.infill = predict(Model6,. , type="response"))

dat_infill %>%
  ggplot() +  
  geom_point(aes(x=xyC(dat_infill)[,1], y=xyC(dat_infill)[,2], colour = factor(ntile(predict_2031.infill,5)))) +
  scale_colour_manual(values = palette5,
                    labels=substr(quintileBreaks(dat_infill,"predict_2031.infill"),1,4),
                    name="Quintile\nBreaks") +
  geom_sf(data=studyAreaCounties, fill=NA, colour="black", size=1) +
  labs(title= "Development Demand in 2031: Predicted Probabilities") +
  mapTheme

11: Comparing Predicted Development Demand & Environmental Sensitivity

11.2: 2021 Land Cover Data

developed21 <- lc_2021 == 21 | lc_2021 == 22 | lc_2021 == 23 | lc_2021 == 24
forest21 <- lc_2021 == 41 | lc_2021 == 42 | lc_2021 == 43 
farm21 <- lc_2021 == 81 | lc_2021 == 82 
wetlands21 <- lc_2021 == 90 | lc_2021 == 95 
otherUndeveloped21 <- lc_2021 == 52 | lc_2021 == 71 | lc_2021 == 31 
water21 <- lc_2021 == 11

names(developed21) <- "developed21"
names(forest21) <- "forest21"
names(farm21) <- "farm21"
names(wetlands21) <- "wetlands21"
names(otherUndeveloped21) <- "otherUndeveloped21"
names(water21) <- "water21"

ggplot() +
  geom_sf(data=dvMSA) +
  geom_raster(data = rbind(rast(lc_2011) %>% mutate(label = "2011"),
                           rast(lc_2021) %>% mutate(label = "2021")) %>% 
              na.omit %>% filter(value > 0), 
              aes(x,y,fill=as.factor(value))) +
  facet_wrap(~label) +
  scale_fill_viridis(discrete=TRUE, name ="") +
  labs(title = "Land Cover, 2011 & 2021") +
  mapTheme + theme(legend.position = "none")

theRasterList21 <- c(developed21,forest21,farm21,wetlands21,otherUndeveloped21,water21)

dat2 <-
  aggregateRaster(theRasterList21, dat) %>%
  dplyr::select(developed21,forest21,farm21,wetlands21,otherUndeveloped21,water21) %>%
  st_set_geometry(NULL) %>%
  bind_cols(.,dat) %>%
  st_sf() %>%
  st_cast("POLYGON")

dat2 %>%
  gather(var,value,developed21:water21) %>%
  st_centroid() %>%
  mutate(X = st_coordinates(.)[,1],
         Y = st_coordinates(.)[,2]) %>%
  ggplot() +
    geom_sf(data=dvMSA) +
    geom_point(aes(X,Y, colour=as.factor(value))) +
    facet_wrap(~var) +
    scale_colour_manual(values = palette2,
                        labels=c("Other","Land Cover"),
                        name = "") +
    labs(title = "Land Cover Types, 2021",
         subtitle = "As fishnet centroids") +
   mapTheme

11.3: Sensitive Land Cover Lost

dat2 <-
  dat2 %>%
   mutate(sensitive_lost21 = ifelse(forest == 1 & forest21 == 0 |
                                    wetlands == 1 & wetlands21 == 0,1,0))
                      
ggplot() +
  geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitive_lost21))) +
  scale_colour_manual(values = palette2,
                      labels=c("No Change","Sensitive Lost"),
                      name = "") +
  labs(title = "Sensitive lands lost: 2011 - 2021",
       subtitle = "As fishnet centroids") +
  mapTheme

11.4: Landscape Fragmentation

sensitiveRegions <- 
  raster::clump(wetlands21 + forest21) %>%
  rasterToPolygons() %>%
  st_as_sf() %>%
  group_by(clumps) %>% 
  summarize() %>%
    mutate(Acres = as.numeric(st_area(.) * 0.0000229568)) %>%
    filter(Acres > 3954)  %>%
  dplyr::select() %>%
  raster::rasterize(.,emptyRaster) 
sensitiveRegions[sensitiveRegions > 0] <- 1  
names(sensitiveRegions) <- "sensitiveRegions"

dat2 <-
  aggregateRaster(c(sensitiveRegions), dat2) %>%
  dplyr::select(sensitiveRegions) %>%
  st_set_geometry(NULL) %>%
  bind_cols(.,dat2) %>%
  st_sf()

ggplot() +
  geom_point(data=dat2, aes(x=xyC(dat2)[,1], y=xyC(dat2)[,2], colour=as.factor(sensitiveRegions))) +
  scale_colour_manual(values = palette2,
                      labels=c("Other","Sensitive Regions"),
                      name="") +
  labs(title = "Sensitive regions",
       subtitle = "Continous areas of either wetlands or forests\ngreater than 1 acre") +
  mapTheme

11.5: Summarize by County

county_specific_metrics <- 
  dat2 %>%
  #predict development demand from our model
  mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
  #get a count count of grid cells by county which we can use to calculate rates below
  left_join(st_set_geometry(dat, NULL) %>% group_by(NAME) %>% summarize(count = n())) %>%
  #calculate summary statistics by county
  group_by(NAME) %>%
  summarize(Total_Farmland = sum(farm21) / max(count),
            Total_Forest = sum(forest21) / max(count),
            Total_Wetlands = sum(wetlands21) / max(count),
            Total_Undeveloped = sum(otherUndeveloped21) / max(count),
            Sensitive_Land_Lost = sum(sensitive_lost21) / max(count),
            Sensitive_Regions = sum(sensitiveRegions) / max(count),
            Mean_Development_Demand = mean(Development_Demand)) %>%
  #get population data by county
  left_join(countyPopulation_2031 %>% 
            mutate(Population_Change = county_projection_2031 - county_population_2021,
                   Population_Change_Rate = Population_Change / county_projection_2031) %>%
            dplyr::select(NAME,Population_Change_Rate)) %>%
  na.omit()
county_specific_metrics %>%
  gather(Variable, Value, -NAME, -geometry) %>%
  mutate(Variable = factor(Variable, levels=c("Population_Change_Rate","Mean_Development_Demand",
                                              "Total_Farmland","Total_Undeveloped","Total_Forest",
                                              "Total_Wetlands","Sensitive_Land_Lost","Sensitive_Regions",
                                              ordered = TRUE))) %>%
  mutate(Planning_Designation = case_when(
    Variable == "Population_Change_Rate" | Variable == "Mean_Development_Demand" ~ "Demand-Side",
    Variable == "Total_Farmland" | Variable == "Total_Undeveloped"               ~ "Suitable",
    TRUE                                                                         ~ "Not Suitable")) %>%
  ggplot(aes(x=Variable, y=Value, fill=Planning_Designation)) +
    geom_bar(stat="identity", position=position_dodge(), colour="black") +
    facet_wrap(~NAME, ncol=5) +
    coord_flip() +
    scale_y_continuous(breaks = seq(.25, 1, by = .25)) +
    geom_vline(xintercept = 2.5) + geom_vline(xintercept = 4.5) +
    scale_fill_manual(values=c("black","red","darkgreen")) +
    labs(title= "County Specific Allocation Metrics", subtitle= "As rates", x="Indicator", y="Rate") +
    plotTheme + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="bottom")

12: Scenario 1: Allocation

chester <-
  dat2 %>%
    mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
    filter(NAME == "Chester") 

chester_landUse <- rbind(
  filter(chester, forest21 == 1 | wetlands21 == 1 ) %>%
  dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
  filter(chester, developed21 == 1) %>%
  dplyr::select() %>% mutate(Land_Use = "Developed"))

grid.arrange(
ggplot() +
  geom_sf(data=chester, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
  geom_point(data=chester_landUse, aes(x=xyC(chester_landUse)[,1], 
                                        y=xyC(chester_landUse)[,2], colour=Land_Use),
                                        shape = 15, size = 2) +
  geom_sf(data=st_intersection(dvHighways,filter(studyAreaCounties, NAME=="Chester")), size=2) +
  scale_fill_manual(values = palette5, name="Development\nDemand",
                    labels=substr(quintileBreaks(chester,"Development_Demand"),1,5)) +
  scale_colour_manual(values = c("black","red")) + 
  labs(title = "Development Potential, 2031: Chester") + mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),

ggplot() +
  geom_sf(data=chester, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
  geom_point(data=chester_landUse, aes(x=xyC(chester_landUse)[,1], 
                                        y=xyC(chester_landUse)[,2], colour=Land_Use),
                                        shape = 15, size = 2) +
  geom_sf(data=st_intersection(dvHighways,filter(studyAreaCounties, NAME=="Chester")), size=2) +
  scale_fill_manual(values = palette5, name="Population\nChange",
                    labels=substr(quintileBreaks(chester,"pop_Change"),1,5)) +
  scale_colour_manual(values = c("black","red")) + 
  labs(title = "Projected Population, 2031: Chester") + mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

13: Scenario 2 Transportation

13.1: Highway Distance

#dvHighways <-
#  st_read("C:/Users/ferna/OneDrive/Documents/ArcGIS/Projects/CPLN 6750/HW5/dv_roads.geojson") %>%
#  st_transform(st_crs(dvMSA)) %>%
#  st_intersection(dvMSA)

dvTransit <-
  st_read("/Users/luyiiwong/Documents/GitHub/LandUseModeling_HW5/DelawareValley/dv_transit.geojson") %>%
  st_transform(st_crs(dvMSA)) %>%
  st_intersection(dvMSA)
ggplot() +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=layer),size=1.5) +
  geom_sf(data=dvTransit) +
  scale_colour_manual(values = palette2,
                      labels=c("No Change","N`w Development")) +
  labs(title = "New Development and New Transit",
       subtitle = "As fishnet centroids") +
  mapTheme

emptyRaster <- lc_change
emptyRaster[] <- NA

dvTransit_spdf <- as(dvTransit, "Spatial")
transit_raster <- rasterize(dvTransit, emptyRaster)

#highway_raster <- 
  #as(dvTransit,'Spatial') %>%
  #rasterize(.,emptyRaster)

transit_raster_distance <- distance(transit_raster)
names(transit_raster_distance) <- "distance_transit"

transitPoints <-
  rasterToPoints(transit_raster_distance) %>%
  as.data.frame() %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(dvMSA_fishnet))

transitPoints_fishnet <- 
  aggregate(transitPoints, dvMSA_fishnet, mean) %>%
  mutate(distance_transit = ifelse(is.na(distance_transit),0,distance_transit))

ggplot() +
  geom_sf(data=dvMSA) +
  geom_point(data=transitPoints_fishnet, aes(x=xyC(transitPoints_fishnet)[,1], 
                                             y=xyC(transitPoints_fishnet)[,2], 
                 colour=factor(ntile(distance_transit,5))),size=1.5) +
  scale_colour_manual(values = palette5,
                      labels=substr(quintileBreaks(transitPoints_fishnet,"distance_transit"),1,8),
                      name="Quintile/nBreaks") +
  geom_sf(data=dvTransit, colour = "red") +
  labs(title = "Distance to transit",
       subtitle = "As fishnet centroids; transit visualized in red") +
  mapTheme

13.2: Create a new dataset for the model

changing the distance from highway to distance to transit

dat.2 <- 
  cbind(fishnet, transitPoints_fishnet, fishnetPopulation, aggregatedRasters)%>%
  dplyr::select(layer, developed, forest, farm, wetlands, otherUndeveloped, water,
                pop_2011, pop_2021, pop_Change, distance_transit,lagDevelopment) %>%
  st_join(studyAreaCounties) %>%
  mutate(developed10 = ifelse(layer == 1 & developed == 1, 0, developed)) %>%
  filter(water == 0) 

13.3: Create training set

set.seed(3456)
trainIndex <- 
  createDataPartition(dat$developed, p = .50,
                                  list = FALSE,
                                  times = 1)
transit.datTrain <- dat.2[ trainIndex,]
transit.datTest  <- dat.2[-trainIndex,]
# test model with transit
transitModel <- glm(layer ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment + pop_Change + 
              distance_transit, 
              family="binomial"(link="logit"), data = transit.datTrain) 
summary(transitModel)
## 
## Call:
## glm(formula = layer ~ wetlands + forest + farm + otherUndeveloped + 
##     lagDevelopment + pop_Change + distance_transit, family = binomial(link = "logit"), 
##     data = transit.datTrain)
## 
## Coefficients:
##                     Estimate Std. Error z value Pr(>|z|)    
## (Intercept)       -9.258e+00  1.385e+00  -6.686 2.30e-11 ***
## wetlands1          5.393e+00  1.537e+00   3.509 0.000449 ***
## forest1            6.425e+00  1.377e+00   4.665 3.08e-06 ***
## farm1              6.984e+00  1.381e+00   5.058 4.24e-07 ***
## otherUndeveloped1  7.782e+00  1.400e+00   5.560 2.69e-08 ***
## lagDevelopment    -2.151e-03  4.284e-04  -5.020 5.16e-07 ***
## pop_Change         9.339e-03  2.463e-03   3.791 0.000150 ***
## distance_transit  -2.513e-04  9.403e-05  -2.672 0.007534 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 1716.3  on 19904  degrees of freedom
## Residual deviance: 1461.8  on 19897  degrees of freedom
## AIC: 1477.8
## 
## Number of Fisher Scoring iterations: 12
#variables are highly significant
#r-square value: 14.8%
testSetProbs.2 <- 
  data.frame(class = datTest$layer,
             probs = predict(transitModel, transit.datTest, type="response")) 
  
ggplot(testSetProbs.2, aes(probs)) +
  geom_density(aes(fill=class), alpha=0.5) +
  scale_fill_manual(values = palette2,
                    labels=c("No Change","New Development")) +
  labs(title = "Histogram of test set predicted probabilities",
       x="Predicted Probabilities",y="Density") +
  plotTheme

13.4: Creating new dataframe for pop projection

dat.new <-
  aggregateRaster(theRasterList21, dat.2) %>%
  dplyr::select(developed21,forest21,farm21,wetlands21,otherUndeveloped21,water21) %>%
  st_set_geometry(NULL) %>%
  bind_cols(.,dat.2) %>%
  st_sf() %>%
  st_cast("POLYGON")

# considering land fragmentation
dat.new <-
  aggregateRaster(c(sensitiveRegions), dat.new) %>%
  dplyr::select(sensitiveRegions) %>%
  st_set_geometry(NULL) %>%
  bind_cols(.,dat.2) %>%
  st_sf()

13.5: Land allocation - population projection and development forcast

chester.new <-
  dat.new %>%
    mutate(Development_Demand = predict(transitModel, dat.new, type="response")) %>%
    filter(NAME == "Chester") 

chester_landUse.new <- rbind(
  filter(chester, forest21 == 1 | wetlands21 == 1 ) %>%
  dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
  filter(chester, developed21 == 1) %>%
  dplyr::select() %>% mutate(Land_Use = "Developed"))

grid.arrange(
ggplot() +
  geom_sf(data=chester.new, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
  geom_point(data=chester_landUse.new, aes(x=xyC(chester_landUse.new)[,1], 
                                        y=xyC(chester_landUse.new)[,2], colour=Land_Use),
                                        shape = 15, size = 2) +
  geom_sf(data=st_intersection(dvHighways,filter(studyAreaCounties, NAME=="Chester")), size=2) +
  scale_fill_manual(values = palette5, name="Development\nDemand",
                    labels=substr(quintileBreaks(chester.new,"Development_Demand"),1,5)) +
  scale_colour_manual(values = c("black","red")) + 
  labs(title = "Development Potential, 2031: Chester",
       subtitle = "Additional Transit") + mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),

ggplot() +
  geom_sf(data=chester.new, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
  geom_point(data=chester_landUse.new, aes(x=xyC(chester_landUse.new)[,1], 
                                        y=xyC(chester_landUse.new)[,2], colour=Land_Use),
                                        shape = 15, size = 2) +
  geom_sf(data=st_intersection(dvHighways,filter(studyAreaCounties, NAME=="Chester")), size=2) +
  scale_fill_manual(values = palette5, name="Population\nChange",
                    labels=substr(quintileBreaks(chester.new,"pop_Change"),1,5)) +
  scale_colour_manual(values = c("black","red")) + 
  labs(title = "Projected Population, 2031: Chester",
       subtitle = "Additional Transit") + mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

LS0tCnRpdGxlOiAiRGVsYXdhcmUgVmFsbGV5IE1TQSBVcmJhbiBHcm93dGggTW9kZWwiCmF1dGhvcjogIkx1IFlpaSBXb25nICYgSmF2aWVyIEZlcm5hbmRleiIKZGF0ZTogIjIwMjQtMDUtMTAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3IgbG9hZF9wYWNrYWdlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkocmFzdGVyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkodGlkeWNlbnN1cykKbGlicmFyeSh0aWdyaXMpCmxpYnJhcnkoRk5OKQojbGlicmFyeShRdWFudFBzeWMpICMgSkUgTm90ZTogaW4gUiA0LjEsIFF1YW50UHN5YyBwYWNrYWdlIG5vdCBhdmFpbGFibGUuCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoeWFyZHN0aWNrKQpsaWJyYXJ5KHBzY2wpCmxpYnJhcnkocGxvdFJPQykgCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShwUk9DKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoaWdyYXBoKQoKcGxvdFRoZW1lIDwtIHRoZW1lKAogIHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAjIFNldCB0aGUgZW50aXJlIGNoYXJ0IHJlZ2lvbiB0byBibGFuawogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLAogIHBsb3QuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgI3BhbmVsLmJvcmRlcj1lbGVtZW50X3JlY3QoY29sb3VyPSIjRjBGMEYwIiksCiAgIyBGb3JtYXQgdGhlIGdyaWQKICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXI9IiNEMEQwRDAiLHNpemU9Ljc1KSwKICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSkKCm1hcFRoZW1lIDwtIHRoZW1lKHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgICAgICAgICAgICBheGlzLmxpbmU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ib3JkZXI9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXIgPSAndHJhbnNwYXJlbnQnKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLCAKICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSwgJ2NtJyksCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCgxLCAiY20iKSwgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMC4yLCAiY20iKSkKCnBhbGV0dGUyIDwtIGMoIiM0MWI2YzQiLCIjMjUzNDk0IikKcGFsZXR0ZTQgPC0gYygiI2ExZGFiNCIsIiM0MWI2YzQiLCIjMmM3ZmI4IiwiIzI1MzQ5NCIpCnBhbGV0dGU1IDwtIGMoIiNmZmZmY2MiLCIjYTFkYWI0IiwiIzQxYjZjNCIsIiMyYzdmYjgiLCIjMjUzNDk0IikKcGFsZXR0ZTEwIDwtIGMoIiNmN2ZjZjAiLCIjZTBmM2RiIiwiI2NjZWJjNSIsIiNhOGRkYjUiLCIjN2JjY2M0IiwKICAgICAgICAgICAgICAgIiM0ZWIzZDMiLCIjMmI4Y2JlIiwiIzA4NjhhYyIsIiMwODQwODEiLCIjZjdmY2YwIikKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiN0aGlzIGZ1bmN0aW9uIGNvbnZlcnRzIGEgY29sdW1uIGluIHRvIHF1aW50aWxlcy4gSXQgaXMgdXNlZCBmb3IgbWFwcGluZy4KcXVpbnRpbGVCcmVha3MgPC0gZnVuY3Rpb24oZGYsdmFyaWFibGUpIHsKICAgIGFzLmNoYXJhY3RlcihxdWFudGlsZShkZltbdmFyaWFibGVdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjKC4wMSwuMiwuNCwuNiwuOCksbmEucm09VCkpCn0KCiNUaGlzIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGNvbnZlcnQgYSBwb2x5Z29uIHNmIHRvIGNlbnRyb2lkcyB4eSBjb29yZHMuCnh5QyA8LSBmdW5jdGlvbihhUG9seWdvblNGKSB7CiAgYXMuZGF0YS5mcmFtZSgKICAgIGNiaW5kKHg9c3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoYVBvbHlnb25TRikpWywxXSwKICAgICAgICAgIHk9c3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoYVBvbHlnb25TRikpWywyXSkpCn0gCgojdGhpcyBmdW5jdGlvbiBjb252ZXJ0IGEgcmFzdGVyIHRvIGEgZGF0YSBmcmFtZSBzbyBpdCBjYW4gYmUgcGxvdHRlZCBpbiBnZ3Bsb3QKcmFzdCA8LSBmdW5jdGlvbihpblJhc3RlcikgewogIGRhdGEuZnJhbWUoCiAgICB4eUZyb21DZWxsKGluUmFzdGVyLCAxOm5jZWxsKGluUmFzdGVyKSksIAogICAgdmFsdWUgPSBnZXRWYWx1ZXMoaW5SYXN0ZXIpKSB9CmBgYAoKIyAxOiBTZXQgdXAKCmBgYHtyIGxvYWRfZGF0YSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9CmR2TVNBIDwtIAogIHN0X3JlYWQoIi9Vc2Vycy9sdXlpaXdvbmcvRG9jdW1lbnRzL0xhbmQgVXNlICYgRW52aXJvbm1lbnRhbCBNb2RlbGluZy9Bc3NpZ25tZW50NS9kdl9jb3VudGllcy9kdl9jb3VudGllcy5zaHAiKSAKCmxjXzIwMTEgPSByYXN0ZXIoIi9Vc2Vycy9sdXlpaXdvbmcvRG9jdW1lbnRzL0xhbmQgVXNlICYgRW52aXJvbm1lbnRhbCBNb2RlbGluZy9Bc3NpZ25tZW50NS9sY18yMDExX2NsaXBfUmVzYW1wbGUudGlmIikKbGNfMjAyMSA9IHJhc3RlcigiL1VzZXJzL2x1eWlpd29uZy9Eb2N1bWVudHMvTGFuZCBVc2UgJiBFbnZpcm9ubWVudGFsIE1vZGVsaW5nL0Fzc2lnbm1lbnQ1L2xjXzIwMjFfY2xpcF9SZXNhbXBsZS50aWYiKQoKbGNfY2hhbmdlIDwtIGxjXzIwMTErbGNfMjAyMQoKYGBgCgojIDI6IENhbGN1bGF0aW5nIExhbmQgQ292ZXIgQ2hhbmdlCmBgYHtyfQojY3JlYXRpbmcgYSBtYXRyaXggdG8gY2xhc3NpZnkgdXJiYW4gYW5kIG5vbi11cmJhbiBsYW5kIHVzZQpyZWNsYXNzTWF0cml4IDwtIAogIG1hdHJpeChjKAogICAgMCwxMiwwLAogICAgMTIsMjQsMSwKICAgIDI0LEluZiwwKSwKICBuY29sPTMsIGJ5cm93PVQpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIHJlY2xhc3NpZnlpbmcgbGFuZCBjb3ZlciBmb3IgYm90aCB5ZWFycwpkZXZlbG9wZWRfMjAxMSA8LSAKICByZWNsYXNzaWZ5KGxjXzIwMTEscmVjbGFzc01hdHJpeCkKCmRldmVsb3BlZF8yMDIxIDwtIAogIHJlY2xhc3NpZnkobGNfMjAyMSxyZWNsYXNzTWF0cml4KQoKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgY2FsY3VsYXRpbmcgbGFuZCB1c2UgY2hhbmdlIGZyb20gMjAxMSB0byAyMDIxCmRldmVsb3BtZW50X2NoYW5nZSA8LSBkZXZlbG9wZWRfMjAxMStkZXZlbG9wZWRfMjAyMQoKIyB0aGUgdmFsdWVzIHRoYXQgYXJlID0xIGluZGljYXRlIHRoYXQgdGhlcmUgd2FzIENIQU5HRSBiZXR3ZWVuIDIwMTEgYW5kIDIwMjEKIyBoaXN0b2dyYW0gc2hvd3MgdGhhdCB0aGVyZSB3ZXJlIHZlcnkgZmV3IHZhbHVlcyBpbiB0aGUgc3R1ZHkgYXJlYQpoaXN0KGRldmVsb3BtZW50X2NoYW5nZSkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgcmVjbGFzc2lmeWluZyB2YWx1ZXMgdGhhdCBhcmUgbm90IGVxdWFsIHRvIDEgdG8gTkEgaW4gZGV2ZWxvcG1lbnQgY2hhbmdlCiMgdGhpcyBtZWFucyB0aGF0IHZhbHVlcyAwIGFuZCAyIGJlY29tZSBOQQpkZXZlbG9wbWVudF9jaGFuZ2VbZGV2ZWxvcG1lbnRfY2hhbmdlICE9IDFdIDwtIE5BCgojIHBsb3R0aW5nIHRoZSB2YWx1ZXMgb2YgZGV2ZWxvcG1lbnQgY2hhbmdlID0gMSBzcGF0aWFsbHkKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChkZXZlbG9wbWVudF9jaGFuZ2UpICU+JSBuYS5vbWl0LCAKICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VFJVRSwgbmFtZSA9IkxhbmQgQ292ZXJcbkNoYW5nZSIpICsgCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgbGFuZCB1c2UgY2hhbmdlIikgKwogIG1hcFRoZW1lCmBgYAoKIyMgMi4xOiBDcmVhdGluZyB0aGUgZmlzaG5ldApgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmR2TVNBX2Zpc2huZXQgPC0gCiAgc3RfbWFrZV9ncmlkKGR2TVNBLCA1MDApICU+JQogIHN0X3NmKCkKCmR2TVNBX2Zpc2huZXQgPC0KICBkdk1TQV9maXNobmV0W2R2TVNBLF0KYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRX0KZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQV9maXNobmV0KSArCiAgbGFicyh0aXRsZT0iRmlzaG5ldCwgNTAwIEZvb3QgUmVzb2x1dGlvbiIpICsKICBtYXBUaGVtZQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KI29yaWdpbmFsIHZlcnNpb24KY2hhbmdlUG9pbnRzIDwtCiAgcmFzdGVyVG9Qb2ludHMoZGV2ZWxvcG1lbnRfY2hhbmdlKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2Nycyhkdk1TQV9maXNobmV0KSkKCiMgY2hhbmdlZCB0aGlzIG5hbWluZyBmcm9tIGxjX2NoYW5nZSB0byBsYXllciB0aGVuIGNvbnZlcnRlZCBOQSB0byAwCiMgYmVjYXVzZSBpbiBmaXNobmV0IGhlcmUsIGl0J3MgY2FsbGVkIGxheWVyIG5vdCBsY19jaGFuZ2UKZmlzaG5ldCA8LSAKICBhZ2dyZWdhdGUoY2hhbmdlUG9pbnRzLCBkdk1TQV9maXNobmV0LCBzdW0pICU+JQogIG11dGF0ZShsYXllciA9IGlmZWxzZShpcy5uYShsYXllciksMCwxKSwKICAgICAgICAgbGF5ZXIgPSBhcy5mYWN0b3IobGF5ZXIpKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KSR4LCB5PXh5QyhmaXNobmV0KSR5LCBjb2xvdXI9bGF5ZXIpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKwogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBEZXZlbG9wbWVudCBDaGFuZ2UiLCBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsKICBtYXBUaGVtZQpgYGAKCiMjIDIuMjogUGxvdHRpbmcgTGFuZCBDb3ZlciBpbiAyMDExCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9ZHZNU0EpICsKICBnZW9tX3Jhc3RlcihkYXRhPXJhc3QobGNfMjAxMSkgJT4lIG5hLm9taXQgJT4lIGZpbHRlcih2YWx1ZSA+IDApLCAKICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VFJVRSwgbmFtZSA9IiIpICsKICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIsIDIwMTEiKSArCiAgbWFwVGhlbWUgKwogIHRoZW1lKGxlZ2VuZC5kaXJlY3Rpb249Imhvcml6b250YWwiKQpgYGAKClRoZSB0YWJsZSBiZWxvdyBzaG93cyB0aGUgYXBwcm9hY2ggdGFrZW4gdG8gcmVjb2RlZCBleGlzdGluZyBsYW5kIGNvdmVyIGNvZGVzIGludG8gdGhlIGNhdGVnb3JpZXMgdXNlZCBpbiBvdXIgYW5hbHlzaXMuIEluIHRoZSBjb2RlIGJsb2NrIGJlbG93IG5ldyByYXN0ZXJzIGFyZSBnZW5lcmF0ZWQgYW5kIGBuYW1lc2AgYXJlIGFwcGxpZWQuIE5hbWluZyBlbnN1cmVzIHRoYXQgd2hlbiB0aGUgcmFzdGVyIGlzIGludGVncmF0ZWQgd2l0aCB0aGUgZmlzaG5ldCwgdGhlIGNvbHVtbiByZWZsZWN0cyB0aGUgYXBwcm9wcmlhdGUgcmFzdGVyLgoKfCBPbGRfQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgfCBOZXdfQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgT3BlbiBTcGFjZSBhcyB3ZWxsIGFzIExvdywgTWVkaXVtIGFuZCBIaWdoIEludGVuc2l0eSBEZXZlbG9wbWVudCB8IERldmVsb3BlZCB8CnwgRGVjaWR1b3VzLCBFdmVyZ3JlZW4sIGFuZCBNaXhlZCBGb3Jlc3QgfCAgRm9yZXN0IHwKfCBQYXN0dXJlL0hheSBhbmQgQ3VsdGl2YXRlZCBDcm9wcyB8IEZhcm0gfAp8IFdvb2R5IGFuZCBFbWVyZ2VudCBIZXJiYWNlb3VzIFdldGxhbmRzIHwgV29vZGxhbmRzIHwKfCBCYXJyZW4gTGFuZCwgRHdhcmYgU2NydWIsIGFuZCBHcmFzc2xhbmQvSGVyYmFjZW91cyB8IE90aGVyIFVuZGV2ZWxvcGVkIHwKfCBXYXRlciB8IFdhdGVyIHwKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGV2ZWxvcGVkIDwtIGxjXzIwMTEgPT0gMjEgfCBsY18yMDExID09IDIyIHwgbGNfMjAxMSA9PSAyMyB8IGxjXzIwMTEgPT0gMjQKZm9yZXN0IDwtIGxjXzIwMTEgPT0gNDEgfCBsY18yMDExID09IDQyIHwgbGNfMjAxMSA9PSA0MyAKZmFybSA8LSBsY18yMDExID09IDgxIHwgbGNfMjAxMSA9PSA4MiAKd2V0bGFuZHMgPC0gbGNfMjAxMSA9PSA5MCB8IGxjXzIwMTEgPT0gOTUgCm90aGVyVW5kZXZlbG9wZWQgPC0gbGNfMjAxMSA9PSA1MiB8IGxjXzIwMTEgPT0gNzEgfCBsY18yMDExID09IDMxIAp3YXRlciA8LSBsY18yMDExID09IDExCgpuYW1lcyhkZXZlbG9wZWQpIDwtICJkZXZlbG9wZWQiCm5hbWVzKGZvcmVzdCkgPC0gImZvcmVzdCIKbmFtZXMoZmFybSkgPC0gImZhcm0iCm5hbWVzKHdldGxhbmRzKSA8LSAid2V0bGFuZHMiCm5hbWVzKG90aGVyVW5kZXZlbG9wZWQpIDwtICJvdGhlclVuZGV2ZWxvcGVkIgpuYW1lcyh3YXRlcikgPC0gIndhdGVyIgpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KCmFnZ3JlZ2F0ZVJhc3RlciA8LSBmdW5jdGlvbihpbnB1dFJhc3Rlckxpc3QsIHRoZUZpc2huZXQpIHsKICAjY3JlYXRlIGFuIGVtcHR5IGZpc2huZXQgd2l0aCB0aGUgc2FtZSBkaW1lbnNpb25zIGFzIHRoZSBpbnB1dCBmaXNobmV0CiAgdGhlc2VGaXNobmV0cyA8LSB0aGVGaXNobmV0ICU+JSBkcGx5cjo6c2VsZWN0KCkKICAjZm9yIGVhY2ggcmFzdGVyIGluIHRoZSByYXN0ZXIgbGlzdAogIGZvciAoaSBpbiBpbnB1dFJhc3Rlckxpc3QpIHsKICAjY3JlYXRlIGEgdmFyaWFibGUgbmFtZSBjb3JyZXNwb25kaW5nIHRvIHRoZSBpdGggcmFzdGVyCiAgdmFyTmFtZSA8LSBuYW1lcyhpKQogICNjb252ZXJ0IHJhc3RlciB0byBwb2ludHMgYXMgYW4gc2YKICAgIHRoZXNlUG9pbnRzIDwtCiAgICAgIHJhc3RlclRvUG9pbnRzKGkpICU+JQogICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpLCBjcnMgPSBzdF9jcnModGhlRmlzaG5ldCkpICU+JQogICAgICBmaWx0ZXIoLltbMV1dID09IDEpCiAgI2FnZ3JlZ2F0ZSB0byB0aGUgZmlzaG5ldAogICAgdGhpc0Zpc2huZXQgPC0KICAgICAgYWdncmVnYXRlKHRoZXNlUG9pbnRzLCB0aGVGaXNobmV0LCBsZW5ndGgpICU+JQogICAgICBtdXRhdGUoISF2YXJOYW1lIDo9IGlmZWxzZShpcy5uYSguW1sxXV0pLDAsMSkpCiAgI2FkZCB0byB0aGUgbGFyZ2VyIGZpc2huZXQKICAgIHRoZXNlRmlzaG5ldHMgPC0gY2JpbmQodGhlc2VGaXNobmV0cyx0aGlzRmlzaG5ldCkKICB9CiAgI291dHB1dCBhbGwgYWdncmVnYXRlcyBhcyBvbmUgbGFyZ2UgZmlzaG5ldAogICByZXR1cm4odGhlc2VGaXNobmV0cykKICB9CmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQp0aGVSYXN0ZXJMaXN0IDwtIGMoZGV2ZWxvcGVkLGZvcmVzdCxmYXJtLHdldGxhbmRzLG90aGVyVW5kZXZlbG9wZWQsd2F0ZXIpCgphZ2dyZWdhdGVkUmFzdGVycyA8LQogIGFnZ3JlZ2F0ZVJhc3Rlcih0aGVSYXN0ZXJMaXN0LCBkdk1TQV9maXNobmV0KSAlPiUKICBkcGx5cjo6c2VsZWN0KGRldmVsb3BlZCxmb3Jlc3QsZmFybSx3ZXRsYW5kcyxvdGhlclVuZGV2ZWxvcGVkLHdhdGVyKSAlPiUKICBtdXRhdGVfaWYoaXMubnVtZXJpYyxhcy5mYWN0b3IpCgphZ2dyZWdhdGVkUmFzdGVycyAlPiUKICBnYXRoZXIodmFyLHZhbHVlLGRldmVsb3BlZDp3YXRlcikgJT4lCiAgc3RfY2FzdCgiUE9MWUdPTiIpICU+JSAgICAjanVzdCB0byBtYWtlIHN1cmUgbm8gd2VpcmQgZ2VvbWV0cmllcyBzbGlwcGVkIGluCiAgbXV0YXRlKFggPSB4eUMoLikkeCwKICAgICAgICAgWSA9IHh5QyguKSR5KSAlPiUKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGRhdGE9ZHZNU0EpICsKICAgIGdlb21fcG9pbnQoYWVzKFgsWSwgY29sb3VyPWFzLmZhY3Rvcih2YWx1ZSkpKSArCiAgICBmYWNldF93cmFwKH52YXIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIkxhbmQgQ292ZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArCiAgICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgVHlwZXMsIDIwMTEiLAogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsKICAgbWFwVGhlbWUKYGBgCgojIDM6IFB1bGxpbmcgRGVtb2dyYXBoaWMgRGF0YQpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIHNldHRpbmcgdXAgYXBpIGtleSAKY2Vuc3VzX2FwaV9rZXkoImI4M2EyM2FmZWU0YThlZDBmYTEzMWU0NDk4NjllNjU3N2I4NzE1MWUiLCBvdmVyd3JpdGUgPSBUUlVFLCBpbnN0YWxsID0gVFJVRSkKYGBgCkRlbGF3YXJlIFZhbGxleSBNU0EsIFBlbm5zeWx2YW5pYSBDb3VudGllczoKCi0gQnVja3MgQ291bnR5Ci0gQ2hlc3RlciBDb3VudHkKLSBEZWxhd2FyZSBDb3VudHkKLSBNb250Z29tZXJ5IENvdW50eQotIFBoaWxhZGVscGhpYSBDb3VudHkKCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cz0naGlkZSd9CiMgcHVsbGluZyBkYXRhIGZyb20gY2Vuc3VzIGZvciAyMDExCmR2cG9wXzIwMTEgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCAKICAgICAgICAgIHZhcmlhYmxlcyA9IGMoIkIwMTAwM18wMDFFIiksIAogICAgICAgICAgeWVhciA9IDIwMTEsIAogICAgICAgICAgc3RhdGUgPSAiUEEiLCAKICAgICAgICAgIGNvdW50eSA9IGMoIkJ1Y2tzIiwgIkNoZXN0ZXIiLCAiRGVsYXdhcmUiLCAiTW9udGdvbWVyeSIsICJQaGlsYWRlbHBoaWEiKSwKICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSwgCiAgICAgICAgICBvdXRwdXQgPSAid2lkZSIpICU+JQogIHJlbmFtZShwb3BfMjAxMSA9IEIwMTAwM18wMDFFKSAlPiUKICBkcGx5cjo6c2VsZWN0KEdFT0lELCBOQU1FLCBwb3BfMjAxMSwgZ2VvbWV0cnkpJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2Nycyhkdk1TQV9maXNobmV0KSkKCgojIHB1bGxpbmcgZGF0YSBmcm9tIGNlbnN1cyBmb3IgMjAyMQpkdnBvcF8yMDIxIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgCiAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJCMDEwMDNfMDAxRSIpLCAKICAgICAgICAgIHllYXIgPSAyMDIxLCAKICAgICAgICAgIHN0YXRlID0gIlBBIiwgCiAgICAgICAgICBjb3VudHkgPSBjKCJCdWNrcyIsICJDaGVzdGVyIiwgIkRlbGF3YXJlIiwgIk1vbnRnb21lcnkiLCAiUGhpbGFkZWxwaGlhIiksCiAgICAgICAgICBnZW9tZXRyeSA9IFRSVUUsIAogICAgICAgICAgb3V0cHV0ID0gIndpZGUiKSAlPiUKICByZW5hbWUocG9wXzIwMjEgPSBCMDEwMDNfMDAxRSkgJT4lCiAgZHBseXI6OnNlbGVjdChHRU9JRCwgTkFNRSwgcG9wXzIwMjEsIGdlb21ldHJ5KSU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZHZNU0FfZmlzaG5ldCkpCgoKCgojIyBncmlkIGFycmFuZ2UgdHJhY3QgMjAxMSB2IDIwMjEKIyBXSFkgYXJlIHZhbHVlcyBOQT8/Pz8KZ3JpZC5hcnJhbmdlKApnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhID0gZHZwb3BfMjAxMSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF8yMDExLDUpKSksIGNvbG91cj1OQSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1xdWludGlsZUJyZWFrcyhkdnBvcF8yMDExLCJwb3BfMjAxMSIpLAogICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsKICBsYWJzKHRpdGxlPSJQb3B1bGF0aW9uLCBEZWx3YXJlIFZhbGxleSBQQSBjb3VudGllcyBieSB0cmFjdDogMjAxMSIpICsKICBtYXBUaGVtZSwKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGEgPSBkdnBvcF8yMDIxLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wXzIwMjEsNSkpKSwgY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXF1aW50aWxlQnJlYWtzKGR2cG9wXzIwMjEsInBvcF8yMDIxIiksCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKwogIGxhYnModGl0bGU9IlBvcHVsYXRpb24sIERlbHdhcmUgVmFsbGV5IFBBIGNvdW50aWVzIGJ5IHRyYWN0OiAyMDIxIikgKwogIG1hcFRoZW1lLCBuY29sPTIpCgpgYGAKCiMjIDMuMTogSW50ZXJwb2xhdGluZyBQb3B1bGF0aW9uIGFuZCBGaXNobmV0CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZHZNU0FfZmlzaG5ldCA8LQogIGR2TVNBX2Zpc2huZXQgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJmaXNobmV0SUQiKSAlPiUgCiAgbXV0YXRlKGZpc2huZXRJRCA9IGFzLm51bWVyaWMoZmlzaG5ldElEKSkgJT4lCiAgZHBseXI6OnNlbGVjdChmaXNobmV0SUQpCgpmaXNobmV0UG9wdWxhdGlvbjExIDwtCiAgc3RfaW50ZXJwb2xhdGVfYXcoZHZwb3BfMjAxMVsicG9wXzIwMTEiXSwgZHZNU0FfZmlzaG5ldCwgZXh0ZW5zaXZlPVRSVUUpICU+JQogIGFzLmRhdGEuZnJhbWUoLikgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJmaXNobmV0SUQiKSAlPiUKICBsZWZ0X2pvaW4oZHZNU0FfZmlzaG5ldCAlPiUKICAgICAgICAgICAgICBtdXRhdGUoZmlzaG5ldElEID0gYXMuY2hhcmFjdGVyKGZpc2huZXRJRCkpLAogICAgICAgICAgICAuLCBieT1jKCJmaXNobmV0SUQiPSdmaXNobmV0SUQnKSkgJT4lIAogIG11dGF0ZShwb3BfMjAxMSA9IHJlcGxhY2VfbmEocG9wXzIwMTEsMCkpICU+JQogIGRwbHlyOjpzZWxlY3QocG9wXzIwMTEpCgpmaXNobmV0UG9wdWxhdGlvbjIxIDwtCiAgc3RfaW50ZXJwb2xhdGVfYXcoZHZwb3BfMjAyMVsicG9wXzIwMjEiXSxkdk1TQV9maXNobmV0LCBleHRlbnNpdmU9VFJVRSkgJT4lCiAgYXMuZGF0YS5mcmFtZSguKSAlPiUKICByb3duYW1lc190b19jb2x1bW4odmFyID0gImZpc2huZXRJRCIpICU+JQogIGxlZnRfam9pbihkdk1TQV9maXNobmV0ICU+JQogICAgICAgICAgICAgIG11dGF0ZShmaXNobmV0SUQgPSBhcy5jaGFyYWN0ZXIoZmlzaG5ldElEKSksCiAgICAgICAgICAgIC4sIGJ5PWMoImZpc2huZXRJRCI9J2Zpc2huZXRJRCcpKSAlPiUgCiAgbXV0YXRlKHBvcF8yMDIxID0gcmVwbGFjZV9uYShwb3BfMjAyMSwwKSkgJT4lCiAgZHBseXI6OnNlbGVjdChwb3BfMjAyMSkKCmZpc2huZXRQb3B1bGF0aW9uIDwtIAogIGNiaW5kKGZpc2huZXRQb3B1bGF0aW9uMTEsZmlzaG5ldFBvcHVsYXRpb24yMSkgJT4lCiAgZHBseXI6OnNlbGVjdChwb3BfMjAxMSxwb3BfMjAyMSkgJT4lCiAgbXV0YXRlKHBvcF9DaGFuZ2UgPSBwb3BfMjAyMSAtIHBvcF8yMDExKQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aD0gMTF9CmdyaWQuYXJyYW5nZSgKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdnBvcF8yMDIxLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wXzIwMjEsNSkpKSxjb2xvdXI9TkEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGR2cG9wXzIwMjEsInBvcF8yMDIxIiksMSw0KSwKICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArCiAgbGFicyh0aXRsZT0iUG9wdWxhdGlvbiwgRGVsYXdhcmUgVmFsbGV5IE1TQSwgUEE6IDIwMjEiLAogICAgICAgc3VidGl0bGU9IlJlcHJlc2VudGVkIGFzIHRyYWN0czsgQm91bmRhcmllcyBvbWl0dGVkIikgKwogIG1hcFRoZW1lLAoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1maXNobmV0UG9wdWxhdGlvbiwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF8yMDIxLDUpKSksY29sb3VyPU5BKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGZpc2huZXRQb3B1bGF0aW9uLCJwb3BfMjAyMSIpLDEsNCksCiAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKwogIGxhYnModGl0bGU9IlBvcHVsYXRpb24sIERlbGF3YXJlIFZhbGxleSBNU0EsIFBBOiAyMDIxIiwKICAgICAgIHN1YnRpdGxlPSJSZXByZXNlbnRlZCBhcyBmaXNobmV0IGdyaWRjZWxsczsgQm91bmRhcmllcyBvbWl0dGVkIikgKwogIG1hcFRoZW1lLCBuY29sPTIpCmBgYAoKIyA0OiBIaWdod2F5IERpc3RhbmNlCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KI2R2SGlnaHdheXMgPC0KIyAgc3RfcmVhZCgiQzovVXNlcnMvZmVybmEvT25lRHJpdmUvRG9jdW1lbnRzL0FyY0dJUy9Qcm9qZWN0cy9DUExOIDY3NTAvSFc1L2R2X3JvYWRzLmdlb2pzb24iKSAlPiUKIyAgc3RfdHJhbnNmb3JtKHN0X2Nycyhkdk1TQSkpICU+JQojICBzdF9pbnRlcnNlY3Rpb24oZHZNU0EpCgpkdkhpZ2h3YXlzIDwtCiAgc3RfcmVhZCgiL1VzZXJzL2x1eWlpd29uZy9Eb2N1bWVudHMvR2l0SHViL0xhbmRVc2VNb2RlbGluZ19IVzUvRGVsYXdhcmVWYWxsZXkvZHZfcm9hZHMuZ2VvanNvbiIpICU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZHZNU0EpKSAlPiUKICBzdF9pbnRlcnNlY3Rpb24oZHZNU0EpCmBgYAoKYGBge3Igd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRX0KZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSxjb2xvdXI9bGF5ZXIpLHNpemU9MS41KSArCiAgZ2VvbV9zZihkYXRhPWR2SGlnaHdheXMpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgYW5kIEhpZ2h3YXlzIiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQplbXB0eVJhc3RlciA8LSBsY19jaGFuZ2UKZW1wdHlSYXN0ZXJbXSA8LSBOQQoKZHZIaWdod2F5c19zcGRmIDwtIGFzKGR2SGlnaHdheXMsICJTcGF0aWFsIikKaGlnaHdheV9yYXN0ZXIgPC0gcmFzdGVyaXplKGR2SGlnaHdheXMsIGVtcHR5UmFzdGVyKQoKI2hpZ2h3YXlfcmFzdGVyIDwtIAogICNhcyhkdkhpZ2h3YXlzLCdTcGF0aWFsJykgJT4lCiAgI3Jhc3Rlcml6ZSguLGVtcHR5UmFzdGVyKQoKaGlnaHdheV9yYXN0ZXJfZGlzdGFuY2UgPC0gZGlzdGFuY2UoaGlnaHdheV9yYXN0ZXIpCm5hbWVzKGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSA8LSAiZGlzdGFuY2VfaGlnaHdheXMiCgpoaWdod2F5UG9pbnRzIDwtCiAgcmFzdGVyVG9Qb2ludHMoaGlnaHdheV9yYXN0ZXJfZGlzdGFuY2UpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKGR2TVNBX2Zpc2huZXQpKQoKaGlnaHdheVBvaW50c19maXNobmV0IDwtIAogIGFnZ3JlZ2F0ZShoaWdod2F5UG9pbnRzLCBkdk1TQV9maXNobmV0LCBtZWFuKSAlPiUKICBtdXRhdGUoZGlzdGFuY2VfaGlnaHdheXMgPSBpZmVsc2UoaXMubmEoZGlzdGFuY2VfaGlnaHdheXMpLDAsZGlzdGFuY2VfaGlnaHdheXMpKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT1oaWdod2F5UG9pbnRzX2Zpc2huZXQsIGFlcyh4PXh5QyhoaWdod2F5UG9pbnRzX2Zpc2huZXQpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKGhpZ2h3YXlQb2ludHNfZmlzaG5ldClbLDJdLCAKICAgICAgICAgICAgICAgICBjb2xvdXI9ZmFjdG9yKG50aWxlKGRpc3RhbmNlX2hpZ2h3YXlzLDUpKSksc2l6ZT0xLjUpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhoaWdod2F5UG9pbnRzX2Zpc2huZXQsImRpc3RhbmNlX2hpZ2h3YXlzIiksMSw4KSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlL25CcmVha3MiKSArCiAgZ2VvbV9zZihkYXRhPWR2SGlnaHdheXMsIGNvbG91ciA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSB0byBIaWdod2F5cyIsCiAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkczsgSGlnaHdheXMgdmlzdWFsaXplZCBpbiByZWQiKSArCiAgbWFwVGhlbWUKYGBgCgojIDU6IFNwYXRpYWwgbGFnCmBgYCB7ciBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0V9Cm5uX2Z1bmN0aW9uIDwtIGZ1bmN0aW9uKG1lYXN1cmVGcm9tLG1lYXN1cmVUbyxrKSB7CiAgI2NvbnZlcnQgdGhlIHNmIGxheWVycyB0byBtYXRyaWNlcwogIG1lYXN1cmVGcm9tX01hdHJpeCA8LQogICAgYXMubWF0cml4KG1lYXN1cmVGcm9tKQogIG1lYXN1cmVUb19NYXRyaXggPC0KICAgIGFzLm1hdHJpeChtZWFzdXJlVG8pCiAgbm4gPC0gICAKICAgIGdldC5rbm54KG1lYXN1cmVUbywgbWVhc3VyZUZyb20sIGspJG5uLmRpc3QKICAgIG91dHB1dCA8LQogICAgYXMuZGF0YS5mcmFtZShubikgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gInRoaXNQb2ludCIpICU+JQogICAgZ2F0aGVyKHBvaW50cywgcG9pbnRfZGlzdGFuY2UsIFYxOm5jb2woLikpICU+JQogICAgYXJyYW5nZShhcy5udW1lcmljKHRoaXNQb2ludCkpICU+JQogICAgZ3JvdXBfYnkodGhpc1BvaW50KSAlPiUKICAgIHN1bW1hcml6ZShwb2ludERpc3RhbmNlID0gbWVhbihwb2ludF9kaXN0YW5jZSkpICU+JQogICAgYXJyYW5nZShhcy5udW1lcmljKHRoaXNQb2ludCkpICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoLXRoaXNQb2ludCkgJT4lCiAgICBwdWxsKCkKICAKICByZXR1cm4ob3V0cHV0KSAgCn0KYGBgCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nID0gRkFMU0V9CmZpc2huZXQkbGFnRGV2ZWxvcG1lbnQgPC0KICAgIG5uX2Z1bmN0aW9uKHh5QyhmaXNobmV0KSwKICAgICAgICAgICAgICAgIHh5QyhmaWx0ZXIoYWdncmVnYXRlZFJhc3RlcnMsZGV2ZWxvcGVkPT0xKSksCiAgICAgICAgICAgICAgICAyKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSwgCiAgICAgICAgICAgICAgICAgY29sb3VyPSBsb2cobGFnRGV2ZWxvcG1lbnQpLCBzaXplPS4wMDEpKSArCiAgbGFicyh0aXRsZSA9ICJTcGF0aWFsIExhZyB0byAyMDExIERldmVsb3BtZW50IiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikKCiNob3cgc2hvdWxkIHdlIGludGVycHJldCB0aGlzPwpoaXN0KGZpc2huZXQkbGFnRGV2ZWxvcG1lbnQpCiMgbWFrZSBoaXN0b2dyYW0KIyB0cnkgdmlyaWRpcwojIGxvZyBjb2xvci9sYWdkZXZlbG9wbWVudAojIGZpbHRlciBvdXQgb2cgZGV2ZWxvcGVkIGNlbGxzIApgYGAKCiMgNjogQ3JlYXRlIE1TQSBDb3VudGllcyAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0Kb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQoKc3R1ZHlBcmVhQ291bnRpZXMgPC0gCiAgY291bnRpZXMoIlBlbm5zeWx2YW5pYSIpICU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoZHZNU0EpKSAlPiUKICBkcGx5cjo6c2VsZWN0KE5BTUUpICU+JQogIC5bc3RfYnVmZmVyKGR2TVNBLC01MDApLCAsIG9wPXN0X2ludGVyc2VjdHNdCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPXN0dWR5QXJlYUNvdW50aWVzKSArCiAgbGFicyh0aXRsZSA9ICJTdHVkeSBBcmVhIENvdW50aWVzIikgKwogIG1hcFRoZW1lCmBgYAoKIyA3OiBDcmVhdGUgdGhlIEZpbmFsIERhdGFzZXQgCk9uY2Ugd2Ugam9pbiB0aGUgZGF0YSBzZXQgd2l0aCBvdXQgY291bnR5IGJvdW5kYXJpZXMsIGFsbCB0aGUgbGNfY2hhbmdlID0xIGlzIGRyb3BwZWQKYGBge3J9CiMgY2FuIHdlIGdvIHRocm91Z2ggdGhpcyBjb2RlIC0gc3BlY2lmaWNhbGx5IHRoZSBtdXRhdGUgZnVuY3Rpb24KIyBtYWtpbmcgc3VyZSBsb2dpYyBpcyByaWdodCwgdGhhdCBjaGFuZ2UgaXMgZnJvbSBub24tZGV2ZWxvcGVkIHRvIGRldmVsb3BlZAojIGJhc2ljYWxseSBpZ25vcmluZyBjbGFzc2lmaWNhdGlvbnMgdGhhdCBhcmUgd3JvbmcKZGF0IDwtIAogIGNiaW5kKGZpc2huZXQsIGhpZ2h3YXlQb2ludHNfZmlzaG5ldCwgZmlzaG5ldFBvcHVsYXRpb24sIGFnZ3JlZ2F0ZWRSYXN0ZXJzKSU+JQogIGRwbHlyOjpzZWxlY3QobGF5ZXIsIGRldmVsb3BlZCwgZm9yZXN0LCBmYXJtLCB3ZXRsYW5kcywgb3RoZXJVbmRldmVsb3BlZCwgd2F0ZXIsCiAgICAgICAgICAgICAgICBwb3BfMjAxMSwgcG9wXzIwMjEsIHBvcF9DaGFuZ2UsIGRpc3RhbmNlX2hpZ2h3YXlzLGxhZ0RldmVsb3BtZW50KSAlPiUKICBzdF9qb2luKHN0dWR5QXJlYUNvdW50aWVzKSAlPiUKICBtdXRhdGUoZGV2ZWxvcGVkMTAgPSBpZmVsc2UobGF5ZXIgPT0gMSAmIGRldmVsb3BlZCA9PSAxLCAwLCBkZXZlbG9wZWQpKSAlPiUKICBmaWx0ZXIod2F0ZXIgPT0gMCkgCmBgYAoKIyA4OiBFeHBsb3JhdHJveSBBbmFseXNpcwpJdCBzZWVtcyBsaWtlIGxhbmQgY2hhbmdlIG9ubHkgb2NjdXJlZCBmcm9tIHVuZGV2ZWxvcGVkIHRvIHdhdGVyLiBUaGVyZWZvcmUsIHRoZXJlIGlzIG5vIG5ldyBkZXZlbG9wbWVudC4uLgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdCAlPiUKICBkcGx5cjo6c2VsZWN0KGRpc3RhbmNlX2hpZ2h3YXlzLGxhZ0RldmVsb3BtZW50LGxheWVyKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtbGF5ZXIsIC1nZW9tZXRyeSkgJT4lCiAgZ2dwbG90KC4sIGFlcyhsYXllciwgVmFsdWUsIGZpbGw9bGF5ZXIpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gInN1bW1hcnkiLCBmdW4ueSA9ICJtZWFuIikgKwogICAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKwogICAgbGFicyh0aXRsZT0iTmV3IERldmVsb3BtZW50IGFzIGEgRnVuY3Rpb24gb2YgdGhlIENvbnRpbnVvdXMgVmFyaWFibGVzIikgKwogICAgcGxvdFRoZW1lIApgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0ICU+JQogIGRwbHlyOjpzZWxlY3QocG9wXzIwMTEscG9wXzIwMjEscG9wX0NoYW5nZSxsYXllcikgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWxheWVyLCAtZ2VvbWV0cnkpICU+JQogIGdncGxvdCguLCBhZXMobGF5ZXIsIFZhbHVlLCBmaWxsPWxheWVyKSkgKyAKICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuLnkgPSAibWVhbiIpICsKICAgIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsKICAgIGxhYnModGl0bGU9Ik5ldyBEZXZlbG9wbWVudCBhcyBhIEZ1bmN0aW9uIG9mIEZhY3RvciBWYXJpYWJsZXMiKSArCiAgICBwbG90VGhlbWUKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdCAlPiUKICBkcGx5cjo6c2VsZWN0KGxheWVyOm90aGVyVW5kZXZlbG9wZWQsZGV2ZWxvcGVkKSAlPiUKICBnYXRoZXIoTGFuZF9Db3Zlcl9UeXBlLCBWYWx1ZSwgLWxheWVyLCAtZ2VvbWV0cnkpICU+JQogICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgICAgZ3JvdXBfYnkobGF5ZXIsIExhbmRfQ292ZXJfVHlwZSkgJT4lCiAgICAgc3VtbWFyaXplKG4gPSBzdW0oYXMubnVtZXJpYyhWYWx1ZSkpKSAlPiUKICAgICB1bmdyb3VwKCkgJT4lCiAgICBtdXRhdGUoQ29udmVyc2lvbl9SYXRlID0gcGFzdGUwKHJvdW5kKDEwMCAqIG4vc3VtKG4pLCAyKSwgIiUiKSkgJT4lCiAgICBmaWx0ZXIobGF5ZXIgPT0gMSkgJT4lCiAgZHBseXI6OnNlbGVjdChMYW5kX0NvdmVyX1R5cGUsQ29udmVyc2lvbl9SYXRlKSAlPiUKICBrYWJsZSgpICU+JSBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGKQpgYGAKCiMgOTogUHJlZGljdGluZyBmb3IgMjAyMSBNb2RlbApgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnNldC5zZWVkKDM0NTYpCnRyYWluSW5kZXggPC0gCiAgY3JlYXRlRGF0YVBhcnRpdGlvbihkYXQkZGV2ZWxvcGVkLCBwID0gLjUwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSAxKQpkYXRUcmFpbiA8LSBkYXRbIHRyYWluSW5kZXgsXQpkYXRUZXN0ICA8LSBkYXRbLXRyYWluSW5kZXgsXQoKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9Ck1vZGVsMSA8LSBnbG0obGF5ZXIgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCwgCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKQoKTW9kZWwyIDwtIGdsbShsYXllciB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQsIAogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikKICAgICAgICAgICAgICAKTW9kZWwzIDwtIGdsbShsYXllciB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyBwb3BfMjAxMSwgCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICAKICAgICAgICAgICAgICAKTW9kZWw0IDwtIGdsbShsYXllciB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyBwb3BfMjAxMSArIAogICAgICAgICAgICAgIHBvcF8yMDIxLCAKICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAgICAgICAgICAgICAKICAgICAgICAgICAgCk1vZGVsNSA8LSBnbG0obGF5ZXIgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgcG9wX0NoYW5nZSwgCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgCk1vZGVsNiA8LSBnbG0obGF5ZXIgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgcG9wX0NoYW5nZSArIAogICAgICAgICAgICAgIGRpc3RhbmNlX2hpZ2h3YXlzLCAKICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pIApgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cz0naGlkZSd9Cm1vZGVsTGlzdCA8LSBwYXN0ZTAoIk1vZGVsIiwgMTo2KQptYXBfZGZjKG1vZGVsTGlzdCwgZnVuY3Rpb24oeClwUjIoZ2V0KHgpKSlbNCxdICU+JQogIHNldE5hbWVzKHBhc3RlMCgiTW9kZWwiLDE6NikpICU+JQogIGdhdGhlcihNb2RlbCxNY0ZhZGRlbikgJT4lCiAgZ2dwbG90KGFlcyhNb2RlbCxNY0ZhZGRlbikpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogICAgbGFicyh0aXRsZT0gIk1jRmFkZGVuIFItU3F1YXJlZCBieSBNb2RlbCIpICsKICAgIHBsb3RUaGVtZQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KdGVzdFNldFByb2JzIDwtIAogIGRhdGEuZnJhbWUoY2xhc3MgPSBkYXRUZXN0JGxheWVyLAogICAgICAgICAgICAgcHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0VGVzdCwgdHlwZT0icmVzcG9uc2UiKSkgCiAgCmdncGxvdCh0ZXN0U2V0UHJvYnMsIGFlcyhwcm9icykpICsKICBnZW9tX2RlbnNpdHkoYWVzKGZpbGw9Y2xhc3MpLCBhbHBoYT0wLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IikpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiB0ZXN0IHNldCBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyIsCiAgICAgICB4PSJQcmVkaWN0ZWQgUHJvYmFiaWxpdGllcyIseT0iRGVuc2l0eSIpICsKICBwbG90VGhlbWUKYGBgCgojIyA5LjE6IEFjY3VyYWN5CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kb3B0aW9ucyh5YXJkc3RpY2suZXZlbnRfZmlyc3QgPSBGQUxTRSkKCnRlc3RTZXRQcm9icyA8LSAKICB0ZXN0U2V0UHJvYnMgJT4lIAogIG11dGF0ZShwcmVkQ2xhc3NfMDUgPSBhcy5mYWN0b3IoaWZlbHNlKHRlc3RTZXRQcm9icyRwcm9icyA+PSAwLjA1ICwxLDApKSwKICAgICAgICAgcHJlZENsYXNzXzE3ID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMkcHJvYnMgPj0gMC4xNyAsMSwwKSkpIAoKdGVzdFNldFByb2JzICU+JQogIGRwbHlyOjpzZWxlY3QoLXByb2JzKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtY2xhc3MpICU+JQogIGdyb3VwX2J5KFZhcmlhYmxlKSAlPiUKICBzdW1tYXJpemUoU2Vuc2l0aXZpdHkgPSByb3VuZCh5YXJkc3RpY2s6OnNlbnNfdmVjKGNsYXNzLGZhY3RvcihWYWx1ZSkpLDIpLAogICAgICAgICAgICBTcGVjaWZpY2l0eSA9IHJvdW5kKHlhcmRzdGljazo6c3BlY192ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMiksCiAgICAgICAgICAgIEFjY3VyYWN5ID0gcm91bmQoeWFyZHN0aWNrOjphY2N1cmFjeV92ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMikpICU+JSAKICBrYWJsZSgpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpwcmVkc0Zvck1hcCA8LSAgICAgICAgIAogIGRhdCAlPiUKICAgIG11dGF0ZShwcm9icyA9IHByZWRpY3QoTW9kZWw2LCBkYXQsIHR5cGU9InJlc3BvbnNlIikgLAogICAgICAgICAgIFRocmVzaG9sZF81X1BjdCA9IGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4wNSAsMSwwKSksCiAgICAgICAgICAgVGhyZXNob2xkXzE3X1BjdCA9ICBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMTcgLDEsMCkpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QobGF5ZXIsVGhyZXNob2xkXzVfUGN0LFRocmVzaG9sZF8xN19QY3QpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLFZhbHVlLCAtZ2VvbWV0cnkpICU+JQogICAgc3RfY2FzdCgiUE9MWUdPTiIpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGg9IDh9CmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGE9cHJlZHNGb3JNYXAsIGFlcyh4PXh5QyhwcmVkc0Zvck1hcClbLDFdLCB5PXh5QyhwcmVkc0Zvck1hcClbLDJdLCBjb2xvdXI9VmFsdWUpKSArCiAgZmFjZXRfd3JhcCh+VmFyaWFibGUpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLCBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMgLSBMb3cgVGhyZXNob2xkIikgKyAKICBtYXBUaGVtZQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KQ29uZnVzaW9uTWF0cml4Lm1ldHJpY3MgPC0KICBkYXQgJT4lCiAgICBtdXRhdGUocHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0LCB0eXBlPSJyZXNwb25zZSIpICwKICAgICAgICAgICBUaHJlc2hvbGRfNV9QY3QgPSBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMDUgLDEsMCkpLAogICAgICAgICAgIFRocmVzaG9sZF8xN19QY3QgPSAgYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjE3ICwxLDApKSkgJT4lCiAgICBtdXRhdGUoVHJ1ZVBfMDUgPSBpZmVsc2UobGF5ZXIgID09IDEgJiBUaHJlc2hvbGRfNV9QY3QgPT0gMSwgMSwwKSwKICAgICAgICAgICBUcnVlTl8wNSA9IGlmZWxzZShsYXllciAgPT0gMCAmIFRocmVzaG9sZF81X1BjdCA9PSAwLCAxLDApLAogICAgICAgICAgIFRydWVQXzE3ID0gaWZlbHNlKGxheWVyICA9PSAxICYgVGhyZXNob2xkXzE3X1BjdCA9PSAxLCAxLDApLAogICAgICAgICAgIFRydWVOXzE3ID0gaWZlbHNlKGxheWVyICA9PSAwICYgVGhyZXNob2xkXzE3X1BjdCA9PSAwLCAxLDApKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLiwgc3RhcnRzX3dpdGgoIlRydWUiKSkgJT4lCiAgICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZ2VvbWV0cnkpICU+JQogICAgc3RfY2FzdCgiUE9MWUdPTiIpIApgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSA4IH0KZ2dwbG90KGRhdGE9Q29uZnVzaW9uTWF0cml4Lm1ldHJpY3MpICsKICBnZW9tX3BvaW50KGFlcyh4PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDFdLCAKICAgICAgICAgICAgICAgICB5PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDJdLCBjb2xvdXIgPSBhcy5mYWN0b3IoVmFsdWUpKSkgKwogIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIkNvcnJlY3QiLCJJbmNvcnJlY3QiKSwKICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMgLSBMb3cgVGhyZXNob2xkIikgKyBtYXBUaGVtZQpgYGAKCgojIDEwOiBQcmVkaWN0aW5nIExhbmQgQ292ZXIgRGVtYW5kIGZvciAyMDMxCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0IDwtCiAgZGF0ICU+JQogIG11dGF0ZShsYWdEZXZlbG9wbWVudCA9IG5uX2Z1bmN0aW9uKHh5QyguKSwgeHlDKGZpbHRlciguLGRldmVsb3BlZDEwID09IDEpKSwyKSkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiNDSEFOR0UgVEhJUyBDT0RFCmNvdW50eVBvcHVsYXRpb25fMjAzMSA8LSAKICBkYXRhLmZyYW1lKAogICBOQU1FID0gCiAgICAgYygiUGhpbGFkZWxwaGlhIiwiQ2hlc3RlciIsIk1vbnRnb21lcnkiLCJCdWNrcyIsIkRlbGF3YXJlIiksCiAgIGNvdW50eV9wcm9qZWN0aW9uXzIwMzEgPSAKICAgICBjKDE2NDM5NzEsIDU5OTkzMiwgODg0Mzg3LCA2NjkyOTksIDU3NzI0OCkpICU+JQogICNDSEFOR0UgTElORSBBQk9WRSAtIGZyb20gYSBzdHVkeSBEVlJQQyBwb3B1bGF0aW9uIHByb2plY3Rpb24KICAgbGVmdF9qb2luKAogICAgIGRhdCAlPiUKICAgICAgIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUKICAgICAgIGdyb3VwX2J5KE5BTUUpICU+JQogICAgICAgc3VtbWFyaXplKGNvdW50eV9wb3B1bGF0aW9uXzIwMjEgPSByb3VuZChzdW0ocG9wXzIwMjEpKSkpCgpjb3VudHlQb3B1bGF0aW9uXzIwMzEgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLFZhbHVlLCAtTkFNRSkgJT4lCiAgZ2dwbG90KGFlcyhyZW9yZGVyKE5BTUUsLVZhbHVlKSxWYWx1ZSkpICsKICBnZW9tX2JhcihhZXMoZmlsbD1WYXJpYWJsZSksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMjAyMSIsIjIwMzEiKSwKICAgICAgICAgICAgICAgICAgICBuYW1lPSJQb3B1bGF0aW9uIikgKwogIGxhYnModGl0bGU9IlBvcHVsYXRpb24gQ2hhbmdlIGJ5IENvdW50eTogMjAyMSAtIDIwMzEiLAogICAgICAgeD0iQ291bnR5IiwgeT0iUG9wdWxhdGlvbiIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgcGxvdFRoZW1lCmBgYAoKIyMgMTAuMTogUHJlZGljdGluZyBEZXZlbG9wbWVudCBEZW1hbmQKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIHNlY3Rpb24gZWRpdGVkCmRhdF9pbmZpbGwgPC0KICBkYXQgJT4lCiAgI2NhbGN1bGF0ZSBwb3B1bGF0aW9uIGNoYW5nZQogICAgbGVmdF9qb2luKGNvdW50eVBvcHVsYXRpb25fMjAzMSkgJT4lCiAgICBtdXRhdGUocHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wID0gcG9wXzIwMjEgLyBjb3VudHlfcG9wdWxhdGlvbl8yMDIxLAogICAgICAgICAgIHBvcF8yMDMxLmluZmlsbCA9IHByb3BvcnRpb25fb2ZfY291bnR5X3BvcCAqIGNvdW50eV9wcm9qZWN0aW9uXzIwMzEsCiAgICAgICAgICAgcG9wX0NoYW5nZSA9IHJvdW5kKHBvcF8yMDMxLmluZmlsbCAtIHBvcF8yMDIxKSwyKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLWNvdW50eV9wcm9qZWN0aW9uXzIwMzEsIC1jb3VudHlfcG9wdWxhdGlvbl8yMDIxLCAKICAgICAgICAgICAgICAgICAgLXByb3BvcnRpb25fb2ZfY291bnR5X3BvcCwgLXBvcF8yMDMxLmluZmlsbCkgJT4lCiAgI3ByZWRpY3QgZm9yIDIwMzEKICAgIG11dGF0ZShwcmVkaWN0XzIwMzEuaW5maWxsID0gcHJlZGljdChNb2RlbDYsLiAsIHR5cGU9InJlc3BvbnNlIikpCgpkYXRfaW5maWxsICU+JQogIGdncGxvdCgpICsgIAogIGdlb21fcG9pbnQoYWVzKHg9eHlDKGRhdF9pbmZpbGwpWywxXSwgeT14eUMoZGF0X2luZmlsbClbLDJdLCBjb2xvdXIgPSBmYWN0b3IobnRpbGUocHJlZGljdF8yMDMxLmluZmlsbCw1KSkpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGRhdF9pbmZpbGwsInByZWRpY3RfMjAzMS5pbmZpbGwiKSwxLDQpLAogICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArCiAgZ2VvbV9zZihkYXRhPXN0dWR5QXJlYUNvdW50aWVzLCBmaWxsPU5BLCBjb2xvdXI9ImJsYWNrIiwgc2l6ZT0xKSArCiAgbGFicyh0aXRsZT0gIkRldmVsb3BtZW50IERlbWFuZCBpbiAyMDMxOiBQcmVkaWN0ZWQgUHJvYmFiaWxpdGllcyIpICsKICBtYXBUaGVtZQpgYGAKCiMgMTE6IENvbXBhcmluZyBQcmVkaWN0ZWQgRGV2ZWxvcG1lbnQgRGVtYW5kICYgRW52aXJvbm1lbnRhbCBTZW5zaXRpdml0eQojIyAxMS4yOiAyMDIxIExhbmQgQ292ZXIgRGF0YQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRldmVsb3BlZDIxIDwtIGxjXzIwMjEgPT0gMjEgfCBsY18yMDIxID09IDIyIHwgbGNfMjAyMSA9PSAyMyB8IGxjXzIwMjEgPT0gMjQKZm9yZXN0MjEgPC0gbGNfMjAyMSA9PSA0MSB8IGxjXzIwMjEgPT0gNDIgfCBsY18yMDIxID09IDQzIApmYXJtMjEgPC0gbGNfMjAyMSA9PSA4MSB8IGxjXzIwMjEgPT0gODIgCndldGxhbmRzMjEgPC0gbGNfMjAyMSA9PSA5MCB8IGxjXzIwMjEgPT0gOTUgCm90aGVyVW5kZXZlbG9wZWQyMSA8LSBsY18yMDIxID09IDUyIHwgbGNfMjAyMSA9PSA3MSB8IGxjXzIwMjEgPT0gMzEgCndhdGVyMjEgPC0gbGNfMjAyMSA9PSAxMQoKbmFtZXMoZGV2ZWxvcGVkMjEpIDwtICJkZXZlbG9wZWQyMSIKbmFtZXMoZm9yZXN0MjEpIDwtICJmb3Jlc3QyMSIKbmFtZXMoZmFybTIxKSA8LSAiZmFybTIxIgpuYW1lcyh3ZXRsYW5kczIxKSA8LSAid2V0bGFuZHMyMSIKbmFtZXMob3RoZXJVbmRldmVsb3BlZDIxKSA8LSAib3RoZXJVbmRldmVsb3BlZDIxIgpuYW1lcyh3YXRlcjIxKSA8LSAid2F0ZXIyMSIKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9ZHZNU0EpICsKICBnZW9tX3Jhc3RlcihkYXRhID0gcmJpbmQocmFzdChsY18yMDExKSAlPiUgbXV0YXRlKGxhYmVsID0gIjIwMTEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFzdChsY18yMDIxKSAlPiUgbXV0YXRlKGxhYmVsID0gIjIwMjEiKSkgJT4lIAogICAgICAgICAgICAgIG5hLm9taXQgJT4lIGZpbHRlcih2YWx1ZSA+IDApLCAKICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsKICBmYWNldF93cmFwKH5sYWJlbCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1UUlVFLCBuYW1lID0iIikgKwogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciwgMjAxMSAmIDIwMjEiKSArCiAgbWFwVGhlbWUgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQp0aGVSYXN0ZXJMaXN0MjEgPC0gYyhkZXZlbG9wZWQyMSxmb3Jlc3QyMSxmYXJtMjEsd2V0bGFuZHMyMSxvdGhlclVuZGV2ZWxvcGVkMjEsd2F0ZXIyMSkKCmRhdDIgPC0KICBhZ2dyZWdhdGVSYXN0ZXIodGhlUmFzdGVyTGlzdDIxLCBkYXQpICU+JQogIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcGVkMjEsZm9yZXN0MjEsZmFybTIxLHdldGxhbmRzMjEsb3RoZXJVbmRldmVsb3BlZDIxLHdhdGVyMjEpICU+JQogIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUKICBiaW5kX2NvbHMoLixkYXQpICU+JQogIHN0X3NmKCkgJT4lCiAgc3RfY2FzdCgiUE9MWUdPTiIpCgpkYXQyICU+JQogIGdhdGhlcih2YXIsdmFsdWUsZGV2ZWxvcGVkMjE6d2F0ZXIyMSkgJT4lCiAgc3RfY2VudHJvaWQoKSAlPiUKICBtdXRhdGUoWCA9IHN0X2Nvb3JkaW5hdGVzKC4pWywxXSwKICAgICAgICAgWSA9IHN0X2Nvb3JkaW5hdGVzKC4pWywyXSkgJT4lCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9zZihkYXRhPWR2TVNBKSArCiAgICBnZW9tX3BvaW50KGFlcyhYLFksIGNvbG91cj1hcy5mYWN0b3IodmFsdWUpKSkgKwogICAgZmFjZXRfd3JhcCh+dmFyKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiT3RoZXIiLCJMYW5kIENvdmVyIiksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKwogICAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyIFR5cGVzLCAyMDIxIiwKICAgICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArCiAgIG1hcFRoZW1lCmBgYAoKIyMgMTEuMzogU2Vuc2l0aXZlIExhbmQgQ292ZXIgTG9zdAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXQyIDwtCiAgZGF0MiAlPiUKICAgbXV0YXRlKHNlbnNpdGl2ZV9sb3N0MjEgPSBpZmVsc2UoZm9yZXN0ID09IDEgJiBmb3Jlc3QyMSA9PSAwIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2V0bGFuZHMgPT0gMSAmIHdldGxhbmRzMjEgPT0gMCwxLDApKQogICAgICAgICAgICAgICAgICAgICAgCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGE9ZGF0MiwgYWVzKHg9eHlDKGRhdDIpWywxXSwgeT14eUMoZGF0MilbLDJdLCBjb2xvdXI9YXMuZmFjdG9yKHNlbnNpdGl2ZV9sb3N0MjEpKSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiU2Vuc2l0aXZlIExvc3QiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKwogIGxhYnModGl0bGUgPSAiU2Vuc2l0aXZlIGxhbmRzIGxvc3Q6IDIwMTEgLSAyMDIxIiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYAoKIyMgMTEuNDogTGFuZHNjYXBlIEZyYWdtZW50YXRpb24KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aD0gNn0Kc2Vuc2l0aXZlUmVnaW9ucyA8LSAKICByYXN0ZXI6OmNsdW1wKHdldGxhbmRzMjEgKyBmb3Jlc3QyMSkgJT4lCiAgcmFzdGVyVG9Qb2x5Z29ucygpICU+JQogIHN0X2FzX3NmKCkgJT4lCiAgZ3JvdXBfYnkoY2x1bXBzKSAlPiUgCiAgc3VtbWFyaXplKCkgJT4lCiAgICBtdXRhdGUoQWNyZXMgPSBhcy5udW1lcmljKHN0X2FyZWEoLikgKiAwLjAwMDAyMjk1NjgpKSAlPiUKICAgIGZpbHRlcihBY3JlcyA+IDM5NTQpICAlPiUKICBkcGx5cjo6c2VsZWN0KCkgJT4lCiAgcmFzdGVyOjpyYXN0ZXJpemUoLixlbXB0eVJhc3RlcikgCnNlbnNpdGl2ZVJlZ2lvbnNbc2Vuc2l0aXZlUmVnaW9ucyA+IDBdIDwtIDEgIApuYW1lcyhzZW5zaXRpdmVSZWdpb25zKSA8LSAic2Vuc2l0aXZlUmVnaW9ucyIKCmRhdDIgPC0KICBhZ2dyZWdhdGVSYXN0ZXIoYyhzZW5zaXRpdmVSZWdpb25zKSwgZGF0MikgJT4lCiAgZHBseXI6OnNlbGVjdChzZW5zaXRpdmVSZWdpb25zKSAlPiUKICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgYmluZF9jb2xzKC4sZGF0MikgJT4lCiAgc3Rfc2YoKQoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1kYXQyLCBhZXMoeD14eUMoZGF0MilbLDFdLCB5PXh5QyhkYXQyKVssMl0sIGNvbG91cj1hcy5mYWN0b3Ioc2Vuc2l0aXZlUmVnaW9ucykpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIlNlbnNpdGl2ZSBSZWdpb25zIiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZSA9ICJTZW5zaXRpdmUgcmVnaW9ucyIsCiAgICAgICBzdWJ0aXRsZSA9ICJDb250aW5vdXMgYXJlYXMgb2YgZWl0aGVyIHdldGxhbmRzIG9yIGZvcmVzdHNcbmdyZWF0ZXIgdGhhbiAxIGFjcmUiKSArCiAgbWFwVGhlbWUKYGBgCgojIyAxMS41OiBTdW1tYXJpemUgYnkgQ291bnR5CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KY291bnR5X3NwZWNpZmljX21ldHJpY3MgPC0gCiAgZGF0MiAlPiUKICAjcHJlZGljdCBkZXZlbG9wbWVudCBkZW1hbmQgZnJvbSBvdXIgbW9kZWwKICBtdXRhdGUoRGV2ZWxvcG1lbnRfRGVtYW5kID0gcHJlZGljdChNb2RlbDYsIGRhdDIsIHR5cGU9InJlc3BvbnNlIikpICU+JQogICNnZXQgYSBjb3VudCBjb3VudCBvZiBncmlkIGNlbGxzIGJ5IGNvdW50eSB3aGljaCB3ZSBjYW4gdXNlIHRvIGNhbGN1bGF0ZSByYXRlcyBiZWxvdwogIGxlZnRfam9pbihzdF9zZXRfZ2VvbWV0cnkoZGF0LCBOVUxMKSAlPiUgZ3JvdXBfYnkoTkFNRSkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkpICU+JQogICNjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIGJ5IGNvdW50eQogIGdyb3VwX2J5KE5BTUUpICU+JQogIHN1bW1hcml6ZShUb3RhbF9GYXJtbGFuZCA9IHN1bShmYXJtMjEpIC8gbWF4KGNvdW50KSwKICAgICAgICAgICAgVG90YWxfRm9yZXN0ID0gc3VtKGZvcmVzdDIxKSAvIG1heChjb3VudCksCiAgICAgICAgICAgIFRvdGFsX1dldGxhbmRzID0gc3VtKHdldGxhbmRzMjEpIC8gbWF4KGNvdW50KSwKICAgICAgICAgICAgVG90YWxfVW5kZXZlbG9wZWQgPSBzdW0ob3RoZXJVbmRldmVsb3BlZDIxKSAvIG1heChjb3VudCksCiAgICAgICAgICAgIFNlbnNpdGl2ZV9MYW5kX0xvc3QgPSBzdW0oc2Vuc2l0aXZlX2xvc3QyMSkgLyBtYXgoY291bnQpLAogICAgICAgICAgICBTZW5zaXRpdmVfUmVnaW9ucyA9IHN1bShzZW5zaXRpdmVSZWdpb25zKSAvIG1heChjb3VudCksCiAgICAgICAgICAgIE1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kID0gbWVhbihEZXZlbG9wbWVudF9EZW1hbmQpKSAlPiUKICAjZ2V0IHBvcHVsYXRpb24gZGF0YSBieSBjb3VudHkKICBsZWZ0X2pvaW4oY291bnR5UG9wdWxhdGlvbl8yMDMxICU+JSAKICAgICAgICAgICAgbXV0YXRlKFBvcHVsYXRpb25fQ2hhbmdlID0gY291bnR5X3Byb2plY3Rpb25fMjAzMSAtIGNvdW50eV9wb3B1bGF0aW9uXzIwMjEsCiAgICAgICAgICAgICAgICAgICBQb3B1bGF0aW9uX0NoYW5nZV9SYXRlID0gUG9wdWxhdGlvbl9DaGFuZ2UgLyBjb3VudHlfcHJvamVjdGlvbl8yMDMxKSAlPiUKICAgICAgICAgICAgZHBseXI6OnNlbGVjdChOQU1FLFBvcHVsYXRpb25fQ2hhbmdlX1JhdGUpKSAlPiUKICBuYS5vbWl0KCkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmNvdW50eV9zcGVjaWZpY19tZXRyaWNzICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1OQU1FLCAtZ2VvbWV0cnkpICU+JQogIG11dGF0ZShWYXJpYWJsZSA9IGZhY3RvcihWYXJpYWJsZSwgbGV2ZWxzPWMoIlBvcHVsYXRpb25fQ2hhbmdlX1JhdGUiLCJNZWFuX0RldmVsb3BtZW50X0RlbWFuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfRmFybWxhbmQiLCJUb3RhbF9VbmRldmVsb3BlZCIsIlRvdGFsX0ZvcmVzdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfV2V0bGFuZHMiLCJTZW5zaXRpdmVfTGFuZF9Mb3N0IiwiU2Vuc2l0aXZlX1JlZ2lvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpKSkgJT4lCiAgbXV0YXRlKFBsYW5uaW5nX0Rlc2lnbmF0aW9uID0gY2FzZV93aGVuKAogICAgVmFyaWFibGUgPT0gIlBvcHVsYXRpb25fQ2hhbmdlX1JhdGUiIHwgVmFyaWFibGUgPT0gIk1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kIiB+ICJEZW1hbmQtU2lkZSIsCiAgICBWYXJpYWJsZSA9PSAiVG90YWxfRmFybWxhbmQiIHwgVmFyaWFibGUgPT0gIlRvdGFsX1VuZGV2ZWxvcGVkIiAgICAgICAgICAgICAgIH4gIlN1aXRhYmxlIiwKICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiAiTm90IFN1aXRhYmxlIikpICU+JQogIGdncGxvdChhZXMoeD1WYXJpYWJsZSwgeT1WYWx1ZSwgZmlsbD1QbGFubmluZ19EZXNpZ25hdGlvbikpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSwgY29sb3VyPSJibGFjayIpICsKICAgIGZhY2V0X3dyYXAofk5BTUUsIG5jb2w9NSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoLjI1LCAxLCBieSA9IC4yNSkpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIuNSkgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LjUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsInJlZCIsImRhcmtncmVlbiIpKSArCiAgICBsYWJzKHRpdGxlPSAiQ291bnR5IFNwZWNpZmljIEFsbG9jYXRpb24gTWV0cmljcyIsIHN1YnRpdGxlPSAiQXMgcmF0ZXMiLCB4PSJJbmRpY2F0b3IiLCB5PSJSYXRlIikgKwogICAgcGxvdFRoZW1lICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpgYGAKCiMgMTI6IFNjZW5hcmlvIDE6IEFsbG9jYXRpb24KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0PSA4LCBmaWcud2lkdGg9IDExfQpjaGVzdGVyIDwtCiAgZGF0MiAlPiUKICAgIG11dGF0ZShEZXZlbG9wbWVudF9EZW1hbmQgPSBwcmVkaWN0KE1vZGVsNiwgZGF0MiwgdHlwZT0icmVzcG9uc2UiKSkgJT4lCiAgICBmaWx0ZXIoTkFNRSA9PSAiQ2hlc3RlciIpIAoKY2hlc3Rlcl9sYW5kVXNlIDwtIHJiaW5kKAogIGZpbHRlcihjaGVzdGVyLCBmb3Jlc3QyMSA9PSAxIHwgd2V0bGFuZHMyMSA9PSAxICkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiTm90IFN1aXRhYmxlIiksCiAgZmlsdGVyKGNoZXN0ZXIsIGRldmVsb3BlZDIxID09IDEpICU+JQogIGRwbHlyOjpzZWxlY3QoKSAlPiUgbXV0YXRlKExhbmRfVXNlID0gIkRldmVsb3BlZCIpKQoKZ3JpZC5hcnJhbmdlKApnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPWNoZXN0ZXIsIGFlcyhmaWxsPWZhY3RvcihudGlsZShEZXZlbG9wbWVudF9EZW1hbmQsNSkpKSwgY29sb3VyPU5BKSArCiAgZ2VvbV9wb2ludChkYXRhPWNoZXN0ZXJfbGFuZFVzZSwgYWVzKHg9eHlDKGNoZXN0ZXJfbGFuZFVzZSlbLDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKGNoZXN0ZXJfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNSwgc2l6ZSA9IDIpICsKICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKGR2SGlnaHdheXMsZmlsdGVyKHN0dWR5QXJlYUNvdW50aWVzLCBOQU1FPT0iQ2hlc3RlciIpKSwgc2l6ZT0yKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IkRldmVsb3BtZW50XG5EZW1hbmQiLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoY2hlc3RlciwiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKyAKICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IFBvdGVudGlhbCwgMjAzMTogQ2hlc3RlciIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Y2hlc3RlciwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF9DaGFuZ2UsNSkpKSwgY29sb3VyPU5BKSArCiAgZ2VvbV9wb2ludChkYXRhPWNoZXN0ZXJfbGFuZFVzZSwgYWVzKHg9eHlDKGNoZXN0ZXJfbGFuZFVzZSlbLDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKGNoZXN0ZXJfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNSwgc2l6ZSA9IDIpICsKICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKGR2SGlnaHdheXMsZmlsdGVyKHN0dWR5QXJlYUNvdW50aWVzLCBOQU1FPT0iQ2hlc3RlciIpKSwgc2l6ZT0yKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IlBvcHVsYXRpb25cbkNoYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhjaGVzdGVyLCJwb3BfQ2hhbmdlIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKyAKICBsYWJzKHRpdGxlID0gIlByb2plY3RlZCBQb3B1bGF0aW9uLCAyMDMxOiBDaGVzdGVyIikgKyBtYXBUaGVtZSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwgY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpLCBuY29sPTIpCmBgYAoKIyAxMzogU2NlbmFyaW8gMiBUcmFuc3BvcnRhdGlvbgojIyAxMy4xOiBIaWdod2F5IERpc3RhbmNlCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KI2R2SGlnaHdheXMgPC0KIyAgc3RfcmVhZCgiQzovVXNlcnMvZmVybmEvT25lRHJpdmUvRG9jdW1lbnRzL0FyY0dJUy9Qcm9qZWN0cy9DUExOIDY3NTAvSFc1L2R2X3JvYWRzLmdlb2pzb24iKSAlPiUKIyAgc3RfdHJhbnNmb3JtKHN0X2Nycyhkdk1TQSkpICU+JQojICBzdF9pbnRlcnNlY3Rpb24oZHZNU0EpCgpkdlRyYW5zaXQgPC0KICBzdF9yZWFkKCIvVXNlcnMvbHV5aWl3b25nL0RvY3VtZW50cy9HaXRIdWIvTGFuZFVzZU1vZGVsaW5nX0hXNS9EZWxhd2FyZVZhbGxleS9kdl90cmFuc2l0Lmdlb2pzb24iKSAlPiUKICBzdF90cmFuc2Zvcm0oc3RfY3JzKGR2TVNBKSkgJT4lCiAgc3RfaW50ZXJzZWN0aW9uKGR2TVNBKQpgYGAKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0V9CmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGE9ZmlzaG5ldCwgCiAgICAgICAgICAgICBhZXMoeD14eUMoZmlzaG5ldClbLDFdLCB5PXh5QyhmaXNobmV0KVssMl0sY29sb3VyPWxheWVyKSxzaXplPTEuNSkgKwogIGdlb21fc2YoZGF0YT1kdlRyYW5zaXQpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5gdyBEZXZlbG9wbWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgYW5kIE5ldyBUcmFuc2l0IiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYApgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmVtcHR5UmFzdGVyIDwtIGxjX2NoYW5nZQplbXB0eVJhc3RlcltdIDwtIE5BCgpkdlRyYW5zaXRfc3BkZiA8LSBhcyhkdlRyYW5zaXQsICJTcGF0aWFsIikKdHJhbnNpdF9yYXN0ZXIgPC0gcmFzdGVyaXplKGR2VHJhbnNpdCwgZW1wdHlSYXN0ZXIpCgojaGlnaHdheV9yYXN0ZXIgPC0gCiAgI2FzKGR2VHJhbnNpdCwnU3BhdGlhbCcpICU+JQogICNyYXN0ZXJpemUoLixlbXB0eVJhc3RlcikKCnRyYW5zaXRfcmFzdGVyX2Rpc3RhbmNlIDwtIGRpc3RhbmNlKHRyYW5zaXRfcmFzdGVyKQpuYW1lcyh0cmFuc2l0X3Jhc3Rlcl9kaXN0YW5jZSkgPC0gImRpc3RhbmNlX3RyYW5zaXQiCgp0cmFuc2l0UG9pbnRzIDwtCiAgcmFzdGVyVG9Qb2ludHModHJhbnNpdF9yYXN0ZXJfZGlzdGFuY2UpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKGR2TVNBX2Zpc2huZXQpKQoKdHJhbnNpdFBvaW50c19maXNobmV0IDwtIAogIGFnZ3JlZ2F0ZSh0cmFuc2l0UG9pbnRzLCBkdk1TQV9maXNobmV0LCBtZWFuKSAlPiUKICBtdXRhdGUoZGlzdGFuY2VfdHJhbnNpdCA9IGlmZWxzZShpcy5uYShkaXN0YW5jZV90cmFuc2l0KSwwLGRpc3RhbmNlX3RyYW5zaXQpKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kdk1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT10cmFuc2l0UG9pbnRzX2Zpc2huZXQsIGFlcyh4PXh5Qyh0cmFuc2l0UG9pbnRzX2Zpc2huZXQpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKHRyYW5zaXRQb2ludHNfZmlzaG5ldClbLDJdLCAKICAgICAgICAgICAgICAgICBjb2xvdXI9ZmFjdG9yKG50aWxlKGRpc3RhbmNlX3RyYW5zaXQsNSkpKSxzaXplPTEuNSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKHRyYW5zaXRQb2ludHNfZmlzaG5ldCwiZGlzdGFuY2VfdHJhbnNpdCIpLDEsOCksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZS9uQnJlYWtzIikgKwogIGdlb21fc2YoZGF0YT1kdlRyYW5zaXQsIGNvbG91ciA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSB0byB0cmFuc2l0IiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzOyB0cmFuc2l0IHZpc3VhbGl6ZWQgaW4gcmVkIikgKwogIG1hcFRoZW1lCmBgYAoKIyMgMTMuMjogQ3JlYXRlIGEgbmV3IGRhdGFzZXQgZm9yIHRoZSBtb2RlbApjaGFuZ2luZyB0aGUgZGlzdGFuY2UgZnJvbSBoaWdod2F5IHRvIGRpc3RhbmNlIHRvIHRyYW5zaXQKYGBge3J9CmRhdC4yIDwtIAogIGNiaW5kKGZpc2huZXQsIHRyYW5zaXRQb2ludHNfZmlzaG5ldCwgZmlzaG5ldFBvcHVsYXRpb24sIGFnZ3JlZ2F0ZWRSYXN0ZXJzKSU+JQogIGRwbHlyOjpzZWxlY3QobGF5ZXIsIGRldmVsb3BlZCwgZm9yZXN0LCBmYXJtLCB3ZXRsYW5kcywgb3RoZXJVbmRldmVsb3BlZCwgd2F0ZXIsCiAgICAgICAgICAgICAgICBwb3BfMjAxMSwgcG9wXzIwMjEsIHBvcF9DaGFuZ2UsIGRpc3RhbmNlX3RyYW5zaXQsbGFnRGV2ZWxvcG1lbnQpICU+JQogIHN0X2pvaW4oc3R1ZHlBcmVhQ291bnRpZXMpICU+JQogIG11dGF0ZShkZXZlbG9wZWQxMCA9IGlmZWxzZShsYXllciA9PSAxICYgZGV2ZWxvcGVkID09IDEsIDAsIGRldmVsb3BlZCkpICU+JQogIGZpbHRlcih3YXRlciA9PSAwKSAKYGBgCgojIyAxMy4zOiBDcmVhdGUgdHJhaW5pbmcgc2V0CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kc2V0LnNlZWQoMzQ1NikKdHJhaW5JbmRleCA8LSAKICBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdCRkZXZlbG9wZWQsIHAgPSAuNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lcyA9IDEpCnRyYW5zaXQuZGF0VHJhaW4gPC0gZGF0LjJbIHRyYWluSW5kZXgsXQp0cmFuc2l0LmRhdFRlc3QgIDwtIGRhdC4yWy10cmFpbkluZGV4LF0KCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIHRlc3QgbW9kZWwgd2l0aCB0cmFuc2l0CnRyYW5zaXRNb2RlbCA8LSBnbG0obGF5ZXIgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgcG9wX0NoYW5nZSArIAogICAgICAgICAgICAgIGRpc3RhbmNlX3RyYW5zaXQsIAogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSB0cmFuc2l0LmRhdFRyYWluKSAKc3VtbWFyeSh0cmFuc2l0TW9kZWwpCiN2YXJpYWJsZXMgYXJlIGhpZ2hseSBzaWduaWZpY2FudAojci1zcXVhcmUgdmFsdWU6IDE0LjglCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQp0ZXN0U2V0UHJvYnMuMiA8LSAKICBkYXRhLmZyYW1lKGNsYXNzID0gZGF0VGVzdCRsYXllciwKICAgICAgICAgICAgIHByb2JzID0gcHJlZGljdCh0cmFuc2l0TW9kZWwsIHRyYW5zaXQuZGF0VGVzdCwgdHlwZT0icmVzcG9uc2UiKSkgCiAgCmdncGxvdCh0ZXN0U2V0UHJvYnMuMiwgYWVzKHByb2JzKSkgKwogIGdlb21fZGVuc2l0eShhZXMoZmlsbD1jbGFzcyksIGFscGhhPTAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSkgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIHRlc3Qgc2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIiwKICAgICAgIHg9IlByZWRpY3RlZCBQcm9iYWJpbGl0aWVzIix5PSJEZW5zaXR5IikgKwogIHBsb3RUaGVtZQpgYGAKCiMjIDEzLjQ6IENyZWF0aW5nIG5ldyBkYXRhZnJhbWUgZm9yIHBvcCBwcm9qZWN0aW9uCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0Lm5ldyA8LQogIGFnZ3JlZ2F0ZVJhc3Rlcih0aGVSYXN0ZXJMaXN0MjEsIGRhdC4yKSAlPiUKICBkcGx5cjo6c2VsZWN0KGRldmVsb3BlZDIxLGZvcmVzdDIxLGZhcm0yMSx3ZXRsYW5kczIxLG90aGVyVW5kZXZlbG9wZWQyMSx3YXRlcjIxKSAlPiUKICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgYmluZF9jb2xzKC4sZGF0LjIpICU+JQogIHN0X3NmKCkgJT4lCiAgc3RfY2FzdCgiUE9MWUdPTiIpCgojIGNvbnNpZGVyaW5nIGxhbmQgZnJhZ21lbnRhdGlvbgpkYXQubmV3IDwtCiAgYWdncmVnYXRlUmFzdGVyKGMoc2Vuc2l0aXZlUmVnaW9ucyksIGRhdC5uZXcpICU+JQogIGRwbHlyOjpzZWxlY3Qoc2Vuc2l0aXZlUmVnaW9ucykgJT4lCiAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQogIGJpbmRfY29scyguLGRhdC4yKSAlPiUKICBzdF9zZigpCgpgYGAKCiMjIDEzLjU6IExhbmQgYWxsb2NhdGlvbiAtIHBvcHVsYXRpb24gcHJvamVjdGlvbiBhbmQgZGV2ZWxvcG1lbnQgZm9yY2FzdApgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQ9IDgsIGZpZy53aWR0aD0gMTF9CmNoZXN0ZXIubmV3IDwtCiAgZGF0Lm5ldyAlPiUKICAgIG11dGF0ZShEZXZlbG9wbWVudF9EZW1hbmQgPSBwcmVkaWN0KHRyYW5zaXRNb2RlbCwgZGF0Lm5ldywgdHlwZT0icmVzcG9uc2UiKSkgJT4lCiAgICBmaWx0ZXIoTkFNRSA9PSAiQ2hlc3RlciIpIAoKY2hlc3Rlcl9sYW5kVXNlLm5ldyA8LSByYmluZCgKICBmaWx0ZXIoY2hlc3RlciwgZm9yZXN0MjEgPT0gMSB8IHdldGxhbmRzMjEgPT0gMSApICU+JQogIGRwbHlyOjpzZWxlY3QoKSAlPiUgbXV0YXRlKExhbmRfVXNlID0gIk5vdCBTdWl0YWJsZSIpLAogIGZpbHRlcihjaGVzdGVyLCBkZXZlbG9wZWQyMSA9PSAxKSAlPiUKICBkcGx5cjo6c2VsZWN0KCkgJT4lIG11dGF0ZShMYW5kX1VzZSA9ICJEZXZlbG9wZWQiKSkKCmdyaWQuYXJyYW5nZSgKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1jaGVzdGVyLm5ldywgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKERldmVsb3BtZW50X0RlbWFuZCw1KSkpLCBjb2xvdXI9TkEpICsKICBnZW9tX3BvaW50KGRhdGE9Y2hlc3Rlcl9sYW5kVXNlLm5ldywgYWVzKHg9eHlDKGNoZXN0ZXJfbGFuZFVzZS5uZXcpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhjaGVzdGVyX2xhbmRVc2UubmV3KVssMl0sIGNvbG91cj1MYW5kX1VzZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE1LCBzaXplID0gMikgKwogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oZHZIaWdod2F5cyxmaWx0ZXIoc3R1ZHlBcmVhQ291bnRpZXMsIE5BTUU9PSJDaGVzdGVyIikpLCBzaXplPTIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iRGV2ZWxvcG1lbnRcbkRlbWFuZCIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhjaGVzdGVyLm5ldywiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKyAKICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IFBvdGVudGlhbCwgMjAzMTogQ2hlc3RlciIsCiAgICAgICBzdWJ0aXRsZSA9ICJBZGRpdGlvbmFsIFRyYW5zaXQiKSArIG1hcFRoZW1lICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLCBjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSksCgpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPWNoZXN0ZXIubmV3LCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wX0NoYW5nZSw1KSkpLCBjb2xvdXI9TkEpICsKICBnZW9tX3BvaW50KGRhdGE9Y2hlc3Rlcl9sYW5kVXNlLm5ldywgYWVzKHg9eHlDKGNoZXN0ZXJfbGFuZFVzZS5uZXcpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhjaGVzdGVyX2xhbmRVc2UubmV3KVssMl0sIGNvbG91cj1MYW5kX1VzZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE1LCBzaXplID0gMikgKwogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oZHZIaWdod2F5cyxmaWx0ZXIoc3R1ZHlBcmVhQ291bnRpZXMsIE5BTUU9PSJDaGVzdGVyIikpLCBzaXplPTIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iUG9wdWxhdGlvblxuQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGNoZXN0ZXIubmV3LCJwb3BfQ2hhbmdlIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKyAKICBsYWJzKHRpdGxlID0gIlByb2plY3RlZCBQb3B1bGF0aW9uLCAyMDMxOiBDaGVzdGVyIiwKICAgICAgIHN1YnRpdGxlID0gIkFkZGl0aW9uYWwgVHJhbnNpdCIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwgbmNvbD0yKQpgYGA=